removing more stdwin users

This commit is contained in:
Guido van Rossum 2000-05-11 14:43:52 +00:00
parent ede8c6eea1
commit dab6cb8f6d
37 changed files with 0 additions and 6107 deletions

View File

@ -1,19 +0,0 @@
Three programs that provide a user interface based upon STDWIN to the
audio device of the SGI 4D/25. These scripts also demonstrate the power
of a set of window interface classes implemented in Python that simplify
the construction of all sorts of buttons, etc.
XXX This hardware is already obsolete; see ../al for examples of audio
XXX on the Indigo and 4D/35.
jukebox Browses a directory full of sound samples and lets you
play selected ones. (Probably not fully functional, it
requires a conversion program.)
rec A tape recorder that lets you record a sound sample,
play it back, and save it to a file. Various options to
set sampling rate, volume etc. When idle it doubles
as a VU meter.
vumeter A VU meter that displays a history of the volume of
sound recently sampled from the microphone.

View File

@ -1,321 +0,0 @@
#! /usr/bin/env python
# JUKEBOX: browse directories full of sampled sound files.
#
# One or more "list windows" display the files and subdirectories of
# the arguments. Double-clicking on a subdirectory opens a new window
# displaying its contents (and so on recursively). Double clicking
# on a file plays it as a sound file (assuming it is one).
#
# Playing is asynchronous: the application keeps listening to events
# while the sample is playing, so you can change the volume (gain)
# during playing, cancel playing or start a new sample right away.
#
# The control window displays the current output gain and a primitive
# "stop button" to cancel the current play request.
#
# Sound files must currently be in Dik Winter's compressed Mac format.
# Since decompression is costly, decompressed samples are saved in
# /usr/tmp/@j* until the application is left. The files are read
# afresh each time, though.
import audio
import sunaudio
import commands
import getopt
import path
import posix
import rand
import stdwin
from stdwinevents import *
import string
import sys
from WindowParent import WindowParent
from HVSplit import VSplit
from Buttons import PushButton
from Sliders import ComplexSlider
# Pathnames
HOME_BIN_SGI = '/ufs/guido/bin/sgi/' # Directory where macsound2sgi lives
DEF_DB = '/ufs/dik/sounds/Mac/HCOM' # Default directory of sounds
# Global variables
class struct: pass # Class to define featureless structures
G = struct() # Holds writable global variables
# Main program
def main():
G.synchronous = 0 # If set, use synchronous audio.write()
G.debug = 0 # If set, print debug messages
G.gain = 75 # Output gain
G.rate = 3 # Sampling rate
G.busy = 0 # Set while asynchronous playing is active
G.windows = [] # List of open windows (except control)
G.mode = 'mac' # Macintosh mode
G.tempprefix = '/usr/tmp/@j' + `rand.rand()` + '-'
#
optlist, args = getopt.getopt(sys.argv[1:], 'dg:r:sSa')
for optname, optarg in optlist:
if optname == '-d':
G.debug = 1
elif optname == '-g':
G.gain = string.atoi(optarg)
if not (0 < G.gain < 256):
raise optarg.error, '-g gain out of range'
elif optname == '-r':
G.rate = string.atoi(optarg)
if not (1 <= G.rate <= 3):
raise optarg.error, '-r rate out of range'
elif optname == '-s':
G.synchronous = 1
elif optname == '-S':
G.mode = 'sgi'
elif optname == '-a':
G.mode = 'sun'
#
if not args:
args = [DEF_DB]
#
G.cw = opencontrolwindow()
for dirname in args:
G.windows.append(openlistwindow(dirname))
#
#
savegain = audio.getoutgain()
try:
# Initialize stdaudio
audio.setoutgain(0)
audio.start_playing('')
dummy = audio.wait_playing()
audio.setoutgain(0)
maineventloop()
finally:
audio.setoutgain(savegain)
audio.done()
clearcache()
def maineventloop():
mouse_events = WE_MOUSE_DOWN, WE_MOUSE_MOVE, WE_MOUSE_UP
while G.windows:
type, w, detail = event = stdwin.getevent()
if w == G.cw.win:
if type == WE_CLOSE:
return
G.cw.dispatch(event)
else:
if type == WE_DRAW:
w.drawproc(w, detail)
elif type in mouse_events:
w.mouse(w, type, detail)
elif type == WE_CLOSE:
w.close(w)
del w, event
else:
if G.debug: print type, w, detail
# Control window -- to set gain and cancel play operations in progress
def opencontrolwindow():
cw = WindowParent().create('Jukebox', (0, 0))
v = VSplit().create(cw)
#
gain = ComplexSlider().define(v)
gain.setminvalmax(0, G.gain, 255)
gain.settexts(' ', ' ')
gain.sethook(gain_setval_hook)
#
stop = PushButton().definetext(v, 'Stop')
stop.hook = stop_hook
#
cw.realize()
return cw
def gain_setval_hook(self):
G.gain = self.val
if G.busy: audio.setoutgain(G.gain)
def stop_hook(self):
if G.busy:
audio.setoutgain(0)
dummy = audio.stop_playing()
G.busy = 0
# List windows -- to display list of files and subdirectories
def openlistwindow(dirname):
list = posix.listdir(dirname)
list.sort()
i = 0
while i < len(list):
if list[i] == '.' or list[i] == '..':
del list[i]
else:
i = i+1
for i in range(len(list)):
name = list[i]
if path.isdir(path.join(dirname, name)):
list[i] = list[i] + '/'
width = maxwidth(list)
width = width + stdwin.textwidth(' ') # XXX X11 stdwin bug workaround
height = len(list) * stdwin.lineheight()
stdwin.setdefwinsize(width, min(height, 500))
w = stdwin.open(dirname)
stdwin.setdefwinsize(0, 0)
w.setdocsize(width, height)
w.drawproc = drawlistwindow
w.mouse = mouselistwindow
w.close = closelistwindow
w.dirname = dirname
w.list = list
w.selected = -1
return w
def maxwidth(list):
width = 1
for name in list:
w = stdwin.textwidth(name)
if w > width: width = w
return width
def drawlistwindow(w, area):
d = w.begindrawing()
d.erase((0, 0), (1000, 10000))
lh = d.lineheight()
h, v = 0, 0
for name in w.list:
d.text((h, v), name)
v = v + lh
showselection(w, d)
def hideselection(w, d):
if w.selected >= 0:
invertselection(w, d)
def showselection(w, d):
if w.selected >= 0:
invertselection(w, d)
def invertselection(w, d):
lh = d.lineheight()
h1, v1 = p1 = 0, w.selected*lh
h2, v2 = p2 = 1000, v1 + lh
d.invert(p1, p2)
def mouselistwindow(w, type, detail):
(h, v), clicks, button = detail[:3]
d = w.begindrawing()
lh = d.lineheight()
if 0 <= v < lh*len(w.list):
i = v / lh
else:
i = -1
if w.selected <> i:
hideselection(w, d)
w.selected = i
showselection(w, d)
if type == WE_MOUSE_DOWN and clicks >= 2 and i >= 0:
name = path.join(w.dirname, w.list[i])
if name[-1:] == '/':
if clicks == 2:
G.windows.append(openlistwindow(name[:-1]))
else:
playfile(name)
def closelistwindow(w):
remove(G.windows, w)
def remove(list, item):
for i in range(len(list)):
if list[i] == item:
del list[i]
break
# Playing tools
cache = {}
def clearcache():
for x in cache.keys():
try:
sts = posix.system('rm -f ' + cache[x])
if sts:
print cmd
print 'Exit status', sts
except:
print cmd
print 'Exception?!'
del cache[x]
def playfile(name):
if G.mode <> 'mac':
tempname = name
elif cache.has_key(name):
tempname = cache[name]
else:
tempname = G.tempprefix + `rand.rand()`
cmd = HOME_BIN_SGI + 'macsound2sgi'
cmd = cmd + ' ' + commands.mkarg(name)
cmd = cmd + ' >' + tempname
if G.debug: print cmd
sts = posix.system(cmd)
if sts:
print cmd
print 'Exit status', sts
stdwin.fleep()
return
cache[name] = tempname
fp = open(tempname, 'r')
try:
hdr = sunaudio.gethdr(fp)
except sunaudio.error, msg:
hdr = ()
if hdr:
data_size = hdr[0]
data = fp.read(data_size)
# XXX this doesn't work yet, need to convert from uLAW!!!
del fp
else:
del fp
data = readfile(tempname)
if G.debug: print len(data), 'bytes read from', tempname
if G.busy:
G.busy = 0
dummy = audio.stop_playing()
#
# Completely reset the audio device
audio.setrate(G.rate)
audio.setduration(0)
audio.setoutgain(G.gain)
#
if G.synchronous:
audio.write(data)
audio.setoutgain(0)
else:
try:
audio.start_playing(data)
G.busy = 1
except:
stdwin.fleep()
del data
def readfile(filename):
return readfp(open(filename, 'r'))
def readfp(fp):
data = ''
while 1:
buf = fp.read(102400) # Reads most samples in one fell swoop
if not buf:
return data
data = data + buf
main()

View File

@ -1,268 +0,0 @@
#! /usr/bin/env python
import sys
import audio
import stdwin
import string
import getopt
from stdwinevents import *
from Buttons import *
from Sliders import *
#from Soundogram import Soundogram
from VUMeter import VUMeter
from WindowParent import WindowParent, MainLoop
from HVSplit import HSplit, VSplit
class TimeOutToggleButton(ToggleButton):
def define(self, parent):
self = ToggleButton.define(self, parent)
self.parent.need_timer(self)
self.timer_hook = 0
return self
def timer(self):
if self.timer_hook:
self.timer_hook(self)
K = 1024
BUFSIZE = 30*8*K
Rates = [0, 32*K, 16*K, 8*K]
Magics = ['', '0032', '0016', '0008']
class Struct: pass
G = Struct()
def main():
#
# Turn off scroll bars
#
stdwin.setdefscrollbars(0, 0)
#
# Set default state
#
G.gain = 60
G.rate = 3
G.nomuting = 0
G.savefile = '@rec'
#
# Set default values
#
G.data = ''
G.playing = 0
G.recording = 0
G.sogram = 0
#
# Parse options
#
optlist, args = getopt.getopt(sys.argv[1:], 'mdg:r:')
#
for optname, optarg in optlist:
if 0: # (So all cases start with elif)
pass
elif optname == '-d':
G.debug = 1
elif optname == '-g':
G.gain = string.atoi(optarg)
if not (0 < G.gain < 256):
raise optarg.error, '-g gain out of range'
elif optname == '-m':
G.nomuting = (not G.nomuting)
elif optname == '-r':
G.rate = string.atoi(optarg)
if not (1 <= G.rate <= 3):
raise optarg.error, '-r rate out of range'
#
if args:
G.savefile = args[0]
#
# Initialize the sound package
#
audio.setoutgain(G.nomuting * G.gain) # Silence the speaker
audio.setrate(G.rate)
#
# Create the WindowParent and VSplit
#
G.window = WindowParent().create('Recorder', (0, 0))
w = G.vsplit = VSplit().create(G.window)
#
# VU-meter
#
G.vubtn = VUMeter().define(w)
#
# Radiobuttons for rates
#
r1btn = RadioButton().definetext(w, '32 K/sec')
r1btn.on_hook = rate_hook
r1btn.rate = 1
#
r2btn = RadioButton().definetext(w, '16 K/sec')
r2btn.on_hook = rate_hook
r2btn.rate = 2
#
r3btn = RadioButton().definetext(w, '8 K/sec')
r3btn.on_hook = rate_hook
r3btn.rate = 3
#
radios = [r1btn, r2btn, r3btn]
r1btn.group = r2btn.group = r3btn.group = radios
for r in radios:
if r.rate == G.rate: r.select(1)
#
# Other controls
#
G.recbtn = TimeOutToggleButton().definetext(w, 'Record')
G.recbtn.on_hook = record_on_hook
G.recbtn.timer_hook = record_timer_hook
G.recbtn.off_hook = record_off_hook
#
G.mutebtn = CheckButton().definetext(w, 'Mute')
G.mutebtn.select(not G.nomuting)
G.mutebtn.hook = mute_hook
#
G.playbtn = TimeOutToggleButton().definetext(w, 'Playback')
G.playbtn.on_hook = play_on_hook
G.playbtn.timer_hook = play_timer_hook
G.playbtn.off_hook = play_off_hook
#
G.gainbtn = ComplexSlider().define(w)
G.gainbtn.settexts(' Volume: ', ' ')
G.gainbtn.setminvalmax(0, G.gain, 255)
G.gainbtn.sethook(gain_hook)
#
G.sizebtn = Label().definetext(w, `len(G.data)` + ' bytes')
#
#G.showbtn = PushButton().definetext(w, 'Sound-o-gram...')
#G.showbtn.hook = show_hook
#
G.savebtn = PushButton().definetext(w, 'Save...')
G.savebtn.hook = save_hook
#
G.quitbtn = PushButton().definetext(w, 'Quit')
G.quitbtn.hook = quit_hook
G.playbtn.enable(0)
G.savebtn.enable(0)
#G.showbtn.enable(0)
start_vu()
G.window.realize()
#
# Event loop
#
MainLoop()
# XXX Disabled...
def show_hook(self):
savetext = self.text
self.settext('Be patient...')
close_sogram()
stdwin.setdefwinsize(400, 300)
win = stdwin.open('Sound-o-gram')
G.sogram = Soundogram().define(win, G.data)
win.buttons = [G.sogram]
self.settext(savetext)
def close_sogram():
if G.sogram:
# Break circular references
G.sogram.win.buttons[:] = []
del G.sogram.win
G.sogram = 0
def mute_hook(self):
G.nomuting = (not self.selected)
audio.setoutgain(G.nomuting * G.gain)
def rate_hook(self):
G.rate = self.rate
audio.setrate(G.rate)
def record_on_hook(self):
stop_vu()
close_sogram()
audio.setrate(G.rate)
audio.setoutgain(G.nomuting * G.gain)
audio.start_recording(BUFSIZE)
G.recording = 1
G.playbtn.enable(0)
G.window.settimer(10 * BUFSIZE / Rates[G.rate])
def record_timer_hook(self):
if G.recording:
if audio.poll_recording():
self.hilite(0)
record_off_hook(self)
else:
self.parent.settimer(5)
def record_off_hook(self):
if not G.recording:
return
G.data = audio.stop_recording()
G.recording = 0
G.sizebtn.settext(`len(G.data)` + ' bytes')
audio.setoutgain(G.nomuting * G.gain)
G.playbtn.enable((len(G.data) > 0))
G.savebtn.enable((len(G.data) > 0))
#G.showbtn.enable((len(G.data) > 0))
G.window.settimer(0)
start_vu()
def play_on_hook(self):
stop_vu()
audio.setrate(G.rate)
audio.setoutgain(G.gain)
audio.start_playing(G.data)
G.playing = 1
G.recbtn.enable(0)
G.window.settimer(max(10 * len(G.data) / Rates[G.rate], 1))
def play_timer_hook(self):
if G.playing:
if audio.poll_playing():
self.hilite(0)
play_off_hook(self)
else:
self.parent.settimer(5)
def play_off_hook(self):
if not G.playing:
return
x = audio.stop_playing()
G.playing = 0
audio.setoutgain(G.nomuting * G.gain)
G.recbtn.enable(1)
G.window.settimer(0)
start_vu()
def gain_hook(self):
G.gain = self.val
if G.playing or G.nomuting: audio.setoutgain(G.gain)
def save_hook(self):
if not G.data:
stdwin.fleep()
else:
prompt = 'Store sampled data on file: '
try:
G.savefile = stdwin.askfile(prompt, G.savefile, 1)
except KeyboardInterrupt:
return
try:
fp = open(G.savefile, 'w')
fp.write(Magics[G.rate] + G.data)
except:
stdwin.message('Cannot create ' + file)
def stop_vu():
G.vubtn.stop()
def start_vu():
G.vubtn.start()
def quit_hook(self):
G.window.delayed_destroy()
try:
main()
finally:
audio.setoutgain(0)

View File

@ -1,35 +0,0 @@
#! /usr/bin/env python
import audio
import stdwin
from VUMeter import VUMeter
from WindowParent import WindowParent
import MainLoop
NBUFS=20
BUFSIZE = NBUFS*48
SCALE=128
class MyVUMeter(VUMeter):
def init_reactivity(self):
self.parent.need_mouse(self)
def mouse_down(self, detail):
if self.enabled:
self.stop()
else:
self.start()
def mouse_move(self, detail): pass
def mouse_up(self, detail): pass
def main():
audio.setrate(3)
audio.setoutgain(0)
w = WindowParent().create('VU Meter', (200, 100))
v = MyVUMeter().define(w)
v.start()
w.realize()
while 1:
w.dispatch(stdwin.getevent())
main()

View File

@ -1,34 +0,0 @@
import stdwingl
import stdwin
from stdwinevents import *
def main():
size = 12
w = stdwin.open('Font chart ' + `size`)
while 1:
type, window, detail = stdwin.getevent()
if type == WE_CLOSE:
break
if type == WE_DRAW:
width, height = w.getwinsize()
d = w.begindrawing()
d.setsize(size)
h, v = 0, 0
for c in range(32, 256):
ch = chr(c)
chw = d.textwidth(ch)
if h + chw > width:
v = v + d.lineheight()
h = 0
if v >= height:
break
d.text((h, v), ch)
h = h + chw
del d
if type == WE_MOUSE_UP:
size = size + 1
w.settitle('Font chart ' + `size`)
w.change((0, 0), (2000, 2000))
main()

View File

@ -1,135 +0,0 @@
# Define drawing operations for GL stdwin
import gl
import fm
from GL import LO_XOR, LO_SRC
from glstdwin import MASK
class DrawingObject:
#
def _init(self, win):
self.fg = win._fg
self.bg = win._bg
self.font = win._font
self.size = win._size
self.width, self.height = win._area[1]
gl.winset(win._gid)
gl.color(self.fg)
return self
#
def setfont(self, fontname):
self.font = fm.findfont(fontname).scalefont(self.size)
#
def setsize(self, size):
ratio = float(size) / float(self.size)
self.size = size
self.font = self.font.scalefont(ratio)
#
def setfgcolor(self, color):
self.fg = color
gl.color(self.fg)
#
def setbgcolor(self, color):
self.bg = color
#
def cliprect(self, area):
#print 'cliprect', area
(left, top), (right, bottom) = area
gl.scrmask(left, right, self.height-bottom, self.height-top)
#
def noclip(self):
#print 'noclip()'
gl.scrmask(0, self.width, 0, self.height)
#
def paint(self, ((left, top), (right, bottom))):
gl.rectf(left, top, right, bottom)
#
def box(self, ((left, top), (right, bottom))):
#print 'box', ((left, top), (right, bottom))
gl.rect(left, top, right, bottom)
#
def circle(self, (h, v), radius):
gl.circ(h, v, radius)
#
def elarc(self, center, (rh, rv), (a1, a2)):
pass # XXX
#
def erase(self, ((left, top), (right, bottom))):
#print 'erase', ((left, top), (right, bottom))
gl.color(self.bg)
gl.rectf(left, top, right, bottom)
gl.color(self.fg)
#
def invert(self, ((left, top), (right, bottom))):
#print 'invert', ((h0, v0), (h1, v1))
gl.logicop(LO_XOR)
gl.color(self.bg)
gl.rectf(left, top, right, bottom)
gl.color(self.fg)
gl.logicop(LO_SRC)
#
def line(self, (h0, v0), (h1, v1)):
#print 'line', ((h0, v0), (h1, v1))
gl.bgnline()
gl.v2i(h0, v0)
gl.v2i(h1, v1)
gl.endline()
#
def xorline(self, (h0, v0), (h1, v1)):
#print 'xorline', ((h0, v0), (h1, v1))
gl.logicop(LO_XOR)
gl.color(self.bg)
gl.bgnline()
gl.v2i(h0, v0)
gl.v2i(h1, v1)
gl.endline()
gl.color(self.fg)
gl.logicop(LO_SRC)
#
def point(self, (h, v)):
#print 'point', (h, v)
gl.bgnpoint()
gl.v2i(h, v)
gl.endpoint()
#
def text(self, (h, v), string):
#print 'text', ((h, v), string)
if h < 0:
# If the point is outside the window
# the whole string isn't drawn.
# Skip the beginning of the string.
# XXX What if the font is bigger than 20 pixels?
i, n = 0, len(string)
while h < -MASK and i < n:
h = h + self.font.getstrwidth(string[i])
i = i + 1
string = string[i:]
gl.cmov2(h, v + self.baseline())
self.font.setfont()
fm.prstr(string)
#
def shade(self, (h, v), percent):
pass # XXX
#
def baseline(self):
(printermatched, fixed_width, xorig, yorig, xsize, ysize, \
height, nglyphs) = self.font.getfontinfo()
return height - yorig
#
def lineheight(self):
(printermatched, fixed_width, xorig, yorig, xsize, ysize, \
height, nglyphs) = self.font.getfontinfo()
return height
#
def textbreak(self, string, width):
# XXX Slooooow!
n = len(string)
nwidth = self.textwidth(string[:n])
while nwidth > width:
n = n-1
nwidth = self.textwidth(string[:n])
return n
#
def textwidth(self, string):
return self.font.getstrwidth(string)
#

View File

@ -1,400 +0,0 @@
# GL STDWIN
#
# See stdwingl for a convenient hack to use this instead of built-in stdwin
# without modifying your application, except for one line in the main file.
#
# Intrinsic differences with built-in stdwin (hard or impossible to fix):
# - Need to call w.close() to close a window !!!
# - Need to call m.close() to remove a menu !!!
# - Doesn't enforce the existence of at most one drawing object
# - No textedit package
# - No X11 selections
#
# Not yet implemented:
# - shade drawing
# - elliptical arc drawing (need to play with transformation)
# - more than one mouse button
# - scroll bars (need to redo viewport handling to get this)
# - partial redraws
# - dialog boxes
# - timer events
# - cursors
#
# Extra features:
# - color (for now, you need to know the colormap index)
import gl
import fm
from GL import *
from DEVICE import *
from stdwinevents import *
# Customizable constants
#
DEF_FONT = 'Times-Roman' # Default font
DEF_SIZE = 12 # Default font size (points)
MASK = 20 # Viewport minus scrmask
# A structure to hold global variables
#
class Struct: pass
G = Struct()
#
G.queue = [] # Pending STDWIN events
G.drawqueue = [] # Windows that need WE_REDRAW
G.windowmap = {} # Map window id to window object
G.windowmap['0'] = None # For convenience
G.focus = None # Input focus
G.fg = BLACK # Foreground color
G.bg = WHITE # Background color
G.def_size = 0, 0 # Default window size
G.def_pos = 0, 0 # Default window position
#
G.size = DEF_SIZE
G.font = fm.findfont(DEF_FONT).scalefont(G.size)
# Initialize GL
#
gl.foreground()
gl.noport()
dummygid = gl.winopen('')
# Ask for all sorts of events
#
# Both REDRAW (= resize and/or redraw!) and INPUTCHANGE are implicitly queued
#qdevice(REDRAW)
#qdevice(INPUTCHANGE)
#
# Keyboard
gl.qdevice(KEYBD)
gl.qdevice(LEFTARROWKEY)
gl.qdevice(RIGHTARROWKEY)
gl.qdevice(UPARROWKEY)
gl.qdevice(DOWNARROWKEY)
gl.qdevice(LEFTALTKEY)
gl.qdevice(RIGHTALTKEY)
#
# Mouse
gl.qdevice(LEFTMOUSE)
#gl.qdevice(MIDDLEMOUSE)
gl.qdevice(RIGHTMOUSE) # Menu button
# NB MOUSEX, MOUSEY events are queued on button down
#
# Window close requests
gl.qdevice(WINQUIT)
gl.qdevice(WINSHUT)
#
# These aren't needed
#gl.qdevice(TIMER0)
#gl.qdevice(WINFREEZE)
#gl.qdevice(WINTHAW)
#gl.qdevice(REDRAWICONIC)
# STDWIN: create a new window
#
def open(title):
h, v = G.def_pos
width, height = G.def_size
if h > 0 or v > 0:
# Choose arbitrary defaults
if h < 0: h = 10
if v < 0: v = 30
if width <= 0: width = 400
if height <= 0: height = 300
gl.prefposition(h, h+width, 1024-v, 1024-v-height)
elif width > 0 or height > 0:
if width <= 0: width = 400
if height <= 0: height = 300
gl.prefsize(width, height)
from glstdwwin import WindowObject
win = WindowObject()._init(title)
G.windowmap[`win._gid`] = win
return win
# STDWIN: set default initial window position (0 means use default)
#
def setdefwinpos(h, v):
G.def_pos = h, v
# STDWIN: set default window size (0 means use default)
#
def setdefwinsize(width, height):
G.def_size = width, height
# STDWIN: beep or ring the bell
#
def fleep():
gl.ringbell()
# STDWIN: set default foreground color
#
def setfgcolor(color):
G.fg = color
# STDWIN: set default background color
#
def setbgcolor(color):
G.bg = color
# STDWIN: get default foreground color
#
def getfgcolor():
return G.fgcolor
# STDWIN: get default background color
#
def getbgcolor():
return G.bgcolor
# Table mapping characters to key codes
#
key2code = key = {}
key['A'] = AKEY
key['B'] = BKEY
key['C'] = CKEY
key['D'] = DKEY
key['E'] = EKEY
key['F'] = FKEY
key['G'] = GKEY
key['H'] = HKEY
key['I'] = IKEY
key['J'] = JKEY
key['K'] = KKEY
key['L'] = LKEY
key['M'] = MKEY
key['N'] = NKEY
key['O'] = OKEY
key['P'] = PKEY
key['Q'] = QKEY
key['R'] = RKEY
key['S'] = SKEY
key['T'] = TKEY
key['U'] = UKEY
key['V'] = VKEY
key['W'] = WKEY
key['X'] = XKEY
key['Y'] = YKEY
key['Z'] = ZKEY
key['0'] = ZEROKEY
key['1'] = ONEKEY
key['2'] = TWOKEY
key['3'] = THREEKEY
key['4'] = FOURKEY
key['5'] = FIVEKEY
key['6'] = SIXKEY
key['7'] = SEVENKEY
key['8'] = EIGHTKEY
key['9'] = NINEKEY
del key
#
code2key = {}
codelist = []
for key in key2code.keys():
code = key2code[key]
code2key[`code`] = key
codelist.append(code)
del key
# STDWIN: wait for the next event
#
commands = {}
commands['\r'] = WC_RETURN
commands['\b'] = WC_BACKSPACE
commands['\t'] = WC_TAB
#
def getevent():
while 1:
#
# Get next event from the processed queue, if any
#
if G.queue:
event = G.queue[0]
del G.queue[0]
#print 'getevent from queue -->', event
return event
#
# Get next event from the draw queue, if any,
# but only if there is nothing in the system queue.
#
if G.drawqueue and not gl.qtest():
win = G.drawqueue[0]
del G.drawqueue[0]
gl.winset(win._gid)
gl.color(win._bg)
gl.clear()
event = WE_DRAW, win, win._area
#print 'getevent from drawqueue -->', event
return event
#
# Get next event from system queue, blocking if necessary
# until one is available.
# Some cases immediately return the event, others do nothing
# or append one or more events to the processed queue.
#
dev, val = gl.qread()
#
if dev == REDRAW:
win = G.windowmap[`val`]
old_area = win._area
win._fixviewport()
win._needredraw()
if old_area <> win._area:
#print 'getevent --> WE_SIZE'
return WE_SIZE, win, None
elif dev == KEYBD:
if val == 3:
raise KeyboardInterrupt # Control-C in window
character = chr(val)
if commands.has_key(character):
return WE_COMMAND, G.focus, commands[character]
return WE_CHAR, G.focus, character
elif dev == LEFTARROWKEY:
if val:
return WE_COMMAND, G.focus, WC_LEFT
elif dev == RIGHTARROWKEY:
if val:
return WE_COMMAND, G.focus, WC_RIGHT
elif dev == UPARROWKEY:
if val:
return WE_COMMAND, G.focus, WC_UP
elif dev == DOWNARROWKEY:
if val:
return WE_COMMAND, G.focus, WC_DOWN
elif dev in (LEFTALTKEY, RIGHTALTKEY):
if val:
for code in codelist:
gl.qdevice(code)
else:
for code in codelist:
gl.unqdevice(code)
elif dev in codelist:
if val:
event = G.focus._doshortcut(code2key[`dev`])
if event:
return event
elif dev == LEFTMOUSE:
G.mousex = gl.getvaluator(MOUSEX)
G.mousey = gl.getvaluator(MOUSEY)
if val:
type = WE_MOUSE_DOWN
gl.qdevice(MOUSEX)
gl.qdevice(MOUSEY)
else:
type = WE_MOUSE_UP
gl.unqdevice(MOUSEX)
gl.unqdevice(MOUSEY)
return _mouseevent(type)
elif dev == MOUSEX:
G.mousex = val
return _mouseevent(WE_MOUSE_MOVE)
elif dev == MOUSEY:
G.mousey = val
return _mouseevent(WE_MOUSE_MOVE)
elif dev == RIGHTMOUSE: # Menu button press/release
if val: # Press
event = G.focus._domenu()
if event:
return event
elif dev == INPUTCHANGE:
if G.focus:
G.queue.append(WE_DEACTIVATE, G.focus, None)
G.focus = G.windowmap[`val`]
if G.focus:
G.queue.append(WE_ACTIVATE, G.focus, None)
elif dev in (WINSHUT, WINQUIT):
return WE_CLOSE, G.windowmap[`val`], None
else:
print '*** qread() --> dev:', dev, 'val:', val
# Helper routine to construct a mouse (up, move or down) event
#
def _mouseevent(type):
gl.winset(G.focus._gid)
orgx, orgy = gl.getorigin()
sizex, sizey = gl.getsize()
x = G.mousex - orgx
y = G.mousey - orgy
return type, G.focus, ((x, sizey-y), 1, 0, 0)
# STDWIN: text measuring functions
def baseline():
(printermatched, fixed_width, xorig, yorig, xsize, ysize, \
height, nglyphs) = G.font.getfontinfo()
return height - yorig
def lineheight():
(printermatched, fixed_width, xorig, yorig, xsize, ysize, \
height, nglyphs) = G.font.getfontinfo()
return height
def textbreak(string, width):
# XXX Slooooow!
n = len(string)
nwidth = textwidth(string[:n])
while nwidth > width:
n = n-1
nwidth = textwidth(string[:n])
return n
def textwidth(string):
return G.font.getstrwidth(string)
# STDWIN: set default font and size
def setfont(fontname):
G.font = fm.findfont(fontname).scalefont(G.size)
def setsize(size):
ratio = float(size) / float(G.size)
G.size = size
G.font = G.font.scalefont(ratio)
# Utility functions
# Exclusive-or of two BYTES
#
def xor(x, y):
a = bits(x)
b = bits(y)
c = [0, 0, 0, 0, 0, 0, 0, 0]
for i in range(8):
c[i] = (a[i] + b[i]) % 2
return stib(c)
# Return the bits of a byte as a list of 8 integers
#
def bits(x):
b = [0, 0, 0, 0, 0, 0, 0, 0]
for i in range(8):
x, b[i] = divmod(x, 2)
return b
# Convert a list of 8 integers (0|1) to a byte
#
def stib(b):
x = 0
shift = 1
for i in range(8):
x = x + b[i]*shift
shift = shift*2
return x

View File

@ -1,62 +0,0 @@
# Define menu operations for GL stdwin
import gl
from glstdwin import key2code
class MenuObject:
#
def _init(self, win, title):
self._win = win
self._title = title
self._items = []
return self
#
def close(self):
self._win.remove(self)
del self._win
#
def additem(self, *args):
if len(args) == 2:
text, shortcut = args
elif len(args) == 1:
text, shortcut = args[0], None
else:
raise TypeError, 'arg count'
self._items.append([text, shortcut, 1, 0])
#
def setitem(self, i, text):
self._items[i][0] = text
#
def enable(self, i, flag):
self._items[i][2] = flag
#
def check(self, i, flag):
self._items[i][3] = flag
#
def _makepup(self, firstitem):
pup = gl.newpup()
if self._title:
gl.addtopup(pup, self._title + '%t', 0)
for item in self._items:
text = item[0]
if not item[2]: # Disabled
text = ' ( ' + text + ' )%x-1'
else:
if item[3]: # Check mark
text = '-> ' + text
else:
text = ' ' + text
if key2code.has_key(item[1]):
text = text + ' [Alt-' + item[1] + ']'
text = text + '%x' + `firstitem`
gl.addtopup(pup, text, 0)
firstitem = firstitem + 1
return pup
#
def _checkshortcut(self, char):
for i in range(len(self._items)):
item = self._items[i]
if item[2] and item[1] == char:
return i
return -1
#

View File

@ -1,139 +0,0 @@
# Define window operations for STDWIN
import gl
from stdwinevents import *
from glstdwin import G # Global variables
from glstdwin import MASK # Tunable constant
class WindowObject:
#
def _init(self, title):
self._docsize = (0, 0)
self._fg = G.fg
self._bg = G.bg
self._title = title
self._font = G.font
self._size = G.size
self._menus = []
self._gid = gl.winopen(title)
gl.winconstraints() # To remove prefsize() effect
self._fixviewport()
self._needredraw()
return self
#
def close(self):
del G.windowmap[`self._gid`]
gl.winclose(self._gid)
self._gid = 0
#
def _needredraw(self):
if self in G.drawqueue:
G.drawqueue.remove(self)
G.drawqueue.append(self)
#
def begindrawing(self):
from glstdwdraw import DrawingObject
return DrawingObject()._init(self)
#
def change(self, area):
self._needredraw()
# XXX Should record the area to be drawn?
#
def gettitle(self):
return self._title
#
def getdocsize(self):
return self._docsize
#
def getorigin(self):
return self._area[0]
#
def getwinsize(self):
return self._area[1]
#
def scroll(self, area, by):
# XXX ought to use gl.rectcopy()
if by <> (0, 0):
self.change(area)
#
def setdocsize(self, docsize):
self._docsize = docsize
#
def setorigin(self, origin):
pass # XXX
#
def settimer(self, decisecs):
pass # XXX
#
def settitle(self, title):
self._title = title
gl.wintitle(title)
#
def show(self, area):
pass # XXX
#
def _fixviewport(self):
#
# Called after redraw or resize, and initially.
#
# Fix the coordinate system so that (0, 0) is top left,
# units are pixels, and positive axes point right and down.
#
# Make the viewport slightly larger than the window,
# and set the screenmask exactly to the window; this
# help fixing character clipping.
#
# Set self._area to the window rectangle in STDWIN coords.
#
gl.winset(self._gid)
gl.reshapeviewport()
x0, x1, y0, y1 = gl.getviewport()
width, height = x1-x0, y1-y0
gl.viewport(x0-MASK, x1+MASK, y0-MASK, y1+MASK)
gl.scrmask(x0, x1, y0, y1)
gl.ortho2(-MASK, width+MASK, height+MASK, -MASK)
self._area = (0, 0), (width, height)
#
def menucreate(self, title):
from glstdwmenu import MenuObject
menu = MenuObject()._init(self, title)
self._menus.append(menu)
return menu
#
def _domenu(self):
if not self._menus:
return None
if len(self._menus) == 1:
pup = self._menus[0]._makepup(0)
val = gl.dopup(pup)
gl.freepup(pup)
if val < 0:
return None
return WE_MENU, self, (self._menus[0], val)
#
# More than one menu: use nested menus.
#
pups = []
firstitem = 0
for menu in self._menus:
pups.append(menu._makepup(firstitem))
firstitem = firstitem + 100
pup = gl.newpup()
for i in range(len(self._menus)):
gl.addtopup(pup, self._menus[i]._title + '%m', pups[i])
val = gl.dopup(pup)
gl.freepup(pup)
for pup in pups:
gl.freepup(pup)
if val < 0:
return None
i_menu, i_item = divmod(val, 100)
return WE_MENU, self, (self._menus[i_menu], i_item)
#
def _doshortcut(self, char):
for menu in self._menus:
i = menu._checkshortcut(char)
if i >= 0:
return WE_MENU, self, (menu, i)
return None
#

View File

@ -1,10 +0,0 @@
# If you put 'import stdwin_gl' in front of the main program of a program
# using stdwin (before it has a chance to import the real stdwin!),
# it will use glstdwin and think it is stdwin.
import sys
if sys.modules.has_key('stdwin'):
raise RuntimeError, 'too late -- stdwin has already been imported'
import glstdwin
sys.modules['stdwin'] = glstdwin

View File

@ -1,43 +0,0 @@
# Try colors -- display all 256 possible colors, with their color index
import stdwingl
import stdwin
from stdwinevents import *
NROWS = 16
NCOLS = 16
def main():
stdwin.setdefwinsize(NCOLS * stdwin.textwidth('12345'), \
NROWS * stdwin.lineheight() * 3)
w = stdwin.open('TestColors')
#
while 1:
type, window, detail = stdwin.getevent()
if type == WE_CLOSE:
print 'Bye.'
break
elif type == WE_SIZE:
w.change((0,0), (10000, 10000))
elif type == WE_DRAW:
width, height = w.getwinsize()
d = w.begindrawing()
for row in range(NROWS):
for col in range(NCOLS):
color = row*NCOLS + col
d.setfgcolor(color)
p = col*width/NCOLS, row*height/NROWS
q = (col+1)*width/NCOLS, \
(row+1)*height/NROWS
d.paint((p, q))
d.setfgcolor(0)
d.box((p, q))
d.text(p, `color`)
p = p[0] , p[1]+ d.lineheight()
d.setfgcolor(7)
d.text(p, `color`)
del d
#
main()

View File

@ -1,70 +0,0 @@
import sys
if len(sys.argv) < 2:
import stdwingl
color = 1
needclose = 1
else:
color = 0
needclose = 0
import stdwin
import time
from stdwinevents import *
from GL import BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE
def main():
#
stdwin.setdefwinsize(300, 300)
stdwin.setdefwinpos(0, 0)
if color: stdwin.setbgcolor(YELLOW)
w1 = stdwin.open('Hello, world')
w1.box = (10, 10), (90, 90)
#
stdwin.setdefwinsize(0, 0)
stdwin.setdefwinpos(50, 50)
if color: stdwin.setbgcolor(GREEN)
w2 = stdwin.open('Second window')
w2.box = (10, 10), (90, 90)
#
while w1 or w2:
type, window, detail = stdwin.getevent()
if type == WE_DRAW:
d = window.begindrawing()
if window == w1:
if color: d.setfgcolor(BLACK)
d.box(((50, 50), (250, 250)))
if color: d.setfgcolor(RED)
d.cliprect(((50, 50), (250, 250)))
d.paint(w1.box)
d.noclip()
if color: d.setfgcolor(BLUE)
d.line((0, 0), w1.box[0])
elif window == w2:
if color: d.setfgcolor(WHITE)
d.box(w2.box)
if color: d.setfgcolor(BLACK)
d.text(w2.box[0], 'Hello world')
else:
print 'Strange draw???', window, detail
del d
elif type == WE_CLOSE:
if needclose: window.close()
if window == w1:
w1 = None
elif window == w2:
w2 = None
else:
print 'weird close event???', window, detail
elif type in (WE_MOUSE_DOWN, WE_MOUSE_MOVE, WE_MOUSE_UP):
h, v = detail[0]
window.box = (h, v), (h+80, v+80)
window.change(((0,0), (2000, 2000)))
elif type == WE_CHAR:
print 'character', `detail`
else:
print type, window, detail
#
main()
print 'Done.'

View File

@ -1,44 +0,0 @@
# Test menus
import stdwingl
import stdwin
from stdwinevents import *
def main():
w = stdwin.open('TestMenus')
#
items1 = 'Aap', 'Noot', 'Mies'
m1 = w.menucreate('Menu-1')
for item in items1:
m1.additem(item, item[0])
#
items2 = 'Wim', 'Zus', 'Jet', 'Teun', 'Vuur'
m2 = w.menucreate('Menu-2')
for item in items2:
m2.additem(item, `len(item)`)
#
m1.enable(1, 0)
m2.check(1, 1)
#
while 1:
type, window, detail = stdwin.getevent()
if type == WE_CLOSE:
break
elif type == WE_DRAW:
d = w.begindrawing()
d.box(((50,50), (100,100)))
del d
elif type == WE_MENU:
mp, i = detail
if mp == m1:
print 'Choice:', items1[i]
elif mp == m2:
print 'Choice:', items2[i]
else:
print 'Not one of my menus!'
elif type == WE_CHAR:
print 'Character', `detail`
#
main()

View File

@ -1,30 +0,0 @@
#! /usr/bin/env python
testlabels = 'Name', 'Address', 'City', 'Country', 'Comments'
def main():
import stdwin
from WindowParent import WindowParent, MainLoop
from FormSplit import FormSplit
from Buttons import Label
from TextEdit import TextEdit
#
stdwin.setdefscrollbars(0, 0)
#
w = WindowParent().create('FormTest', (0, 0))
f = FormSplit().create(w)
#
h, v = 0, 0
for label in testlabels:
f.placenext(h, v)
lbl = Label().definetext(f, label)
f.placenext(h + 100, v)
txt = TextEdit().createboxed(f, (40, 2), (2, 2))
#txt = TextEdit().create(f, (40, 2))
v = v + 2*stdwin.lineheight() + 10
#
w.realize()
#
MainLoop()
main()

View File

@ -1,18 +0,0 @@
Contents of this directory:
FormTest.py Show how a form can be built to enter multiple fields
RadioGroups.py Show how to use multiple groups of radio buttons
TestCSplit.py Test CSplit widget (a clock-like split)
TestDirList.py Test DirList widget (lists directory contents)
TestFormSplit.py Test FormSplit widget (arbitrary grouping)
TestSched.py Test WindowSched widget (event scheduling)
TestTextEdit.py Test TextEdit widget (probably doen't work any more)
clock.py A simple clock, with alarm
jukebox.py Play audio files (SGI only, needs SOX and SFPLAY)
lpwin.py Watch line printer queues
microedit.py The smallest window editor
miniedit.py A small multi-window editor
python.py A window interface to the Python interpreter
wdiff.py A window-based directory diff
See ../ibrowse for another demo written using stdwin.

View File

@ -1,98 +0,0 @@
#! /usr/bin/env python
# radiogroups.py
#
# Demonstrate multiple groups of radio buttons
import stdwin
from Buttons import *
from WindowParent import WindowParent, MainLoop
from HVSplit import HSplit, VSplit
def main():
#
# Create the widget hierarchy, top-down
#
# 1. Create the window
#
window = WindowParent().create('Radio Groups', (0, 0))
#
# 2. Create a horizontal split to contain the groups
#
topsplit = HSplit().create(window)
#
# 3. Create vertical splits, one for each group
#
group1 = VSplit().create(topsplit)
group2 = VSplit().create(topsplit)
group3 = VSplit().create(topsplit)
#
# 4. Create individual radio buttons, each in their own split
#
b11 = RadioButton().definetext(group1, 'Group 1 button 1')
b12 = RadioButton().definetext(group1, 'Group 1 button 2')
b13 = RadioButton().definetext(group1, 'Group 1 button 3')
#
b21 = RadioButton().definetext(group2, 'Group 2 button 1')
b22 = RadioButton().definetext(group2, 'Group 2 button 2')
b23 = RadioButton().definetext(group2, 'Group 2 button 3')
#
b31 = RadioButton().definetext(group3, 'Group 3 button 1')
b32 = RadioButton().definetext(group3, 'Group 3 button 2')
b33 = RadioButton().definetext(group3, 'Group 3 button 3')
#
# 5. Define the grouping for the radio buttons.
# Note: this doesn't have to be the same as the
# grouping is splits (although it usually is).
# Also set the 'hook' procedure for each button
#
list1 = [b11, b12, b13]
list2 = [b21, b22, b23]
list3 = [b31, b32, b33]
#
for b in list1:
b.group = list1
b.on_hook = myhook
for b in list2:
b.group = list2
b.on_hook = myhook
for b in list3:
b.group = list3
b.on_hook = myhook
#
# 6. Select a default button in each group
#
b11.select(1)
b22.select(1)
b33.select(1)
#
# 6. Realize the window
#
window.realize()
#
# 7. Dispatch events until the window is closed
#
MainLoop()
#
# 8. Report final selections
#
print 'You selected the following choices:'
if b11.selected: print '1.1'
if b12.selected: print '1.2'
if b13.selected: print '1.3'
if b21.selected: print '2.1'
if b22.selected: print '2.2'
if b23.selected: print '2.3'
if b31.selected: print '3.1'
if b32.selected: print '3.2'
if b33.selected: print '3.3'
# My 'hook' procedure
# This is placed as 'hook' attribute on each button.
# The example just prints the title of the selected button.
#
def myhook(self):
print 'Selected:', self.text
main()

View File

@ -1,25 +0,0 @@
#! /usr/bin/env python
# TestCSplit
import stdwin
from WindowParent import WindowParent, MainLoop
from Buttons import PushButton
def main(n):
from CSplit import CSplit
#
stdwin.setdefscrollbars(0, 0)
#
the_window = WindowParent().create('TestCSplit', (0, 0))
the_csplit = CSplit().create(the_window)
#
for i in range(n):
the_child = PushButton().define(the_csplit)
the_child.settext(`(i+n-1)%n+1`)
#
the_window.realize()
#
MainLoop()
main(12)

View File

@ -1,18 +0,0 @@
#! /usr/bin/env python
# TestDirList
from DirList import DirListWindow
from WindowParent import MainLoop
def main():
import sys
args = sys.argv[1:]
if not args:
args = ['.']
# Mac: args = [':']
for arg in args:
w = DirListWindow().create(arg)
MainLoop()
main()

View File

@ -1,27 +0,0 @@
#! /usr/bin/env python
# TestFormSplit
import stdwin
from WindowParent import WindowParent, MainLoop
from Buttons import PushButton
def main(n):
from FormSplit import FormSplit
#
stdwin.setdefscrollbars(1, 1)
#
the_window = WindowParent().create('TestFormSplit', (0, 0))
the_form = FormSplit().create(the_window)
#
for i in range(n):
if i % 3 == 0:
the_form.placenext(i*40, 0)
the_child = PushButton().define(the_form)
the_child.settext('XXX-' + `i` + '-YYY')
#
the_window.realize()
#
MainLoop()
main(6)

View File

@ -1,38 +0,0 @@
#! /usr/bin/env python
# TestSched
import stdwin
from WindowParent import WindowParent, MainLoop
import WindowSched
from Buttons import PushButton
def my_ringer(child):
child.my_id = None
stdwin.fleep()
def my_hook(child):
# schedule for the bell to ring in N seconds; cancel previous
if child.my_id:
WindowSched.cancel(child.my_id)
child.my_id = \
WindowSched.enter(child.my_number*1000, 0, my_ringer, (child,))
def main(n):
from CSplit import CSplit
window = WindowParent().create('TestSched', (0, 0))
csplit = CSplit().create(window)
for i in range(n):
child = PushButton().define(csplit)
child.my_number = i
child.my_id = None
child.settext(`(i+n-1)%n+1`)
child.hook = my_hook
window.realize()
WindowSched.run()
main(12)

View File

@ -1,13 +0,0 @@
#! /usr/bin/env python
# Test TextEdit widgets
def main():
from TextEdit import TextEdit
from WindowParent import WindowParent, MainLoop
w = WindowParent().create('Test TextEdit', (0, 0))
t = TextEdit().create(w, (40, 4))
w.realize()
MainLoop()
main()

View File

@ -1,193 +0,0 @@
#! /usr/bin/env python
# 'clock' -- A simple alarm clock
# The alarm can be set at 5 minute intervals on a 12 hour basis.
# It is controlled with the mouse:
# - Click and drag around the circle to set the alarm.
# - Click far outside the circle to clear the alarm.
# - Click near the center to set the alarm at the last time set.
# The alarm time is indicated by a small triangle just outside the circle,
# and also by a digital time at the bottom.
# The indicators disappear when the alarm is not set.
# When the alarm goes off, it beeps every minute for five minutes,
# and the clock turns into inverse video.
# Click or activate the window to turn the ringing off.
import stdwin
from stdwinevents import WE_MOUSE_DOWN, WE_MOUSE_MOVE, WE_MOUSE_UP, \
WE_TIMER, WE_DRAW, WE_SIZE, WE_CLOSE, WE_ACTIVATE
import mainloop
import time
from math import sin, cos, atan2, pi, sqrt
DEFWIDTH, DEFHEIGHT = 200, 200
MOUSE_EVENTS = (WE_MOUSE_DOWN, WE_MOUSE_MOVE, WE_MOUSE_UP)
ORIGIN = 0, 0
FARAWAY = 2000, 2000
EVERYWHERE = ORIGIN, FARAWAY
def main():
win = makewindow()
del win
mainloop.mainloop()
def makewindow():
stdwin.setdefwinsize(DEFWIDTH, DEFHEIGHT + stdwin.lineheight())
win = stdwin.open('clock')
setdimensions(win)
win.set = 1 # True when alarm is set
win.time = 11*60 + 40 # Time when alarm must go off
win.ring = 0 # True when alarm is ringing
win.dispatch = cdispatch
mainloop.register(win)
settimer(win)
return win
def cdispatch(event):
type, win, detail = event
if type == WE_DRAW:
drawproc(win, detail)
elif type == WE_TIMER:
settimer(win)
drawproc(win, EVERYWHERE)
elif type in MOUSE_EVENTS:
mouseclick(win, type, detail)
elif type == WE_ACTIVATE:
if win.ring:
# Turn the ringing off
win.ring = 0
win.begindrawing().invert(win.mainarea)
elif type == WE_SIZE:
win.change(EVERYWHERE)
setdimensions(win)
elif type == WE_CLOSE:
mainloop.unregister(win)
win.close()
def setdimensions(win):
width, height = win.getwinsize()
height = height - stdwin.lineheight()
if width < height: size = width
else: size = height
halfwidth = width/2
halfheight = height/2
win.center = halfwidth, halfheight
win.radius = size*45/100
win.width = width
win.height = height
win.corner = width, height
win.mainarea = ORIGIN, win.corner
win.lineheight = stdwin.lineheight()
win.farcorner = width, height + win.lineheight
win.statusarea = (0, height), win.farcorner
win.fullarea = ORIGIN, win.farcorner
def settimer(win):
now = time.time()
hours, minutes, seconds = win.times = calctime(now)
delay = 61 - seconds
win.settimer(10 * delay)
minutes = minutes + hours*60
if win.ring:
# Is it time to stop the alarm ringing?
since = (minutes - win.time + 720) % 720
if since >= 5:
# Stop it now
win.ring = 0
else:
# Ring again, once every minute
stdwin.fleep()
elif win.set and minutes == win.time:
# Start the alarm ringing
win.ring = 1
stdwin.fleep()
def drawproc(win, area):
hours, minutes, seconds = win.times
d = win.begindrawing()
d.cliprect(area)
d.erase(EVERYWHERE)
d.circle(win.center, win.radius)
d.line(win.center, calcpoint(win, hours*30 + minutes/2, 0.6))
d.line(win.center, calcpoint(win, minutes*6, 1.0))
str = "%02d:%02d" % (hours, minutes)
p = (win.width - d.textwidth(str))/2, win.height * 3 / 4
d.text(p, str)
if win.set:
drawalarm(win, d)
drawalarmtime(win, d)
if win.ring:
d.invert(win.mainarea)
def mouseclick(win, type, detail):
d = win.begindrawing()
if win.ring:
# First turn the ringing off
win.ring = 0
d.invert(win.mainarea)
h, v = detail[0]
ch, cv = win.center
x, y = h-ch, cv-v
dist = sqrt(x*x + y*y) / float(win.radius)
if dist > 1.2:
if win.set:
drawalarm(win, d)
erasealarmtime(win, d)
win.set = 0
elif dist < 0.8:
if not win.set:
win.set = 1
drawalarm(win, d)
drawalarmtime(win, d)
else:
# Convert to half-degrees (range 0..720)
alpha = atan2(y, x)
hdeg = alpha*360.0/pi
hdeg = 180.0 - hdeg
hdeg = (hdeg + 720.0) % 720.0
atime = 5*int(hdeg/5.0 + 0.5)
if atime <> win.time or not win.set:
if win.set:
drawalarm(win, d)
erasealarmtime(win, d)
win.set = 1
win.time = atime
drawalarm(win, d)
drawalarmtime(win, d)
def drawalarm(win, d):
p1 = calcpoint(win, float(win.time)/2.0, 1.02)
p2 = calcpoint(win, float(win.time)/2.0 - 4.0, 1.1)
p3 = calcpoint(win, float(win.time)/2.0 + 4.0, 1.1)
d.xorline(p1, p2)
d.xorline(p2, p3)
d.xorline(p3, p1)
def erasealarmtime(win, d):
d.erase(win.statusarea)
def drawalarmtime(win, d):
# win.time is in the range 0..720 with origin at 12 o'clock
# Convert to hours (0..12) and minutes (12*(0..60))
hh = win.time/60
mm = win.time%60
str = 'Alarm@%02d:%02d' % (hh, mm)
p1 = (win.width - d.textwidth(str))/2, win.height
d.text(p1, str)
def calcpoint(win, degrees, size):
alpha = pi/2.0 - float(degrees) * pi/180.0
x, y = cos(alpha), sin(alpha)
h, v = win.center
r = float(win.radius)
return h + int(x*size*r), v - int(y*size*r)
def calctime(now):
hours, minutes, seconds = time.localtime(now)[3:6]
hours = hours % 12
return hours, minutes, seconds
main()

View File

@ -1,34 +0,0 @@
This directory contains a browser written in Python for "Info files"
as used by the Emacs documentation system. The browser requires that
Python is built with the "stdwin" option and runs under X11 or the
Mac window system.
Now you can read Info files even if you can't spare the memory, time or
disk space to run Emacs. (I have used this extensively on a Macintosh
with 1 Megabyte main memory and a 20 Meg harddisk.)
You can give this to someone with great fear of complex computer
systems, as long as they can use a mouse.
Another reason to use this is to encourage the use of Info for on-line
documentation of software that is not related to Emacs or GNU.
(In particular, I plan to redo the Python and STDWIN documentation
in texinfo.)
The main program is in file "ib.py"; this accepts a file name and a
node name as optional command line arguments, i.e., its usage is
python ib.py [file [node]]
Configuration:
- The pathname of the directory (or directories) containing
the standard Info files should be set by editing the
value assigned to INFOPATH in module ifile.py.
- The default font should be set by editing the value of FONT
in this module (ibrowse.py).
- For fastest I/O, you may look at BLOCKSIZE and a few other
constants in ifile.py.

View File

@ -1,62 +0,0 @@
-*- Text -*-
This is the file .../ibrowse/dir, which contains the topmost node of the
Info hierarchy. The first time you invoke Ibrowse you start off
looking at that node, which is (dir)Top. (This is a copy of the Info
dir node, except that the reference to Info is replaced by one to Ibrowse.)

File: dir Node: Top This is the top of the INFO tree
This (the Directory node) gives a menu of major topics.
Typing "d" returns here, "q" exits, "?" lists all INFO commands, "h"
gives a primer for first-timers, "mTexinfo<Return>" visits Texinfo topic,
etc.
--- PLEASE ADD DOCUMENTATION TO THIS TREE. (See INFO topic first.) ---
* Menu: The list of major topics begins on the next line.
* Ibrowse: (ibrowse). Documentation browsing system.
* Emacs: (emacs). The extensible self-documenting text editor.
* VIP: (vip). A VI-emulation for Emacs.
* Texinfo: (texinfo).
With one source file, make either a printed manual
(through TeX) or an Info file (through texinfo).
Full documentation in this menu item.
* Termcap: (termcap).
The termcap library, which enables application programs
to handle all types of character-display terminals.
* Regex: (regex).
The GNU regular expression library.
* Bison: (bison.info).
The GNU yacc-compatible parser generator.
* GCC: (gcc.info).
The GNU C compiler.
* G++: (g-whiz).
The GNU C++ compiler.
* LibG++: (libg++).
The GNU C++ library.
* GDB: (gdb.info).
The GNU debugger.
* CPP: (cpp.info).
The GNU C preprocessor.
* Lispref: (lispref).
The GNU Emacs Lisp reference manual.
* Make: (make-info).
The GNU make program.
* M4: (m4).
The GNU m4 program.
* Gawk: (gawk-info).
GNU awk.

View File

@ -1,2 +0,0 @@
: ${ARCH}=`arch`
exec /ufs/guido/bin/$ARCH/python ib.py ${1+"$@"}

View File

@ -1,21 +0,0 @@
#! /usr/bin/env python
# Call ibrowse (the info file browser) under UNIX.
import sys
import ibrowse
if len(sys.argv) > 1:
file = sys.argv[1]
if len(sys.argv) > 2:
if len(sys.argv) > 3:
sys.stdout = sys.stderr
print 'usage:', sys.argv[0], '[file [node]]'
sys.exit(2)
else:
node = sys.argv[2]
else:
node = ''
ibrowse.start('(' + file + ')' + node)
else:
ibrowse.main()

View File

@ -1,719 +0,0 @@
This file documents the ibrowse program. -*-Text-*-
The H command of ibrowse goes to the node Help in this file.

File: ibrowse Node: Top Up: (DIR) Next: Expert
Ibrowse is a program for reading documentation, which you are using now.
** Ibrowse uses the file format of the Emacs Info program, and its
** commands are similar, but not identical.
To learn how to use Ibrowse, type the command "h". It will bring you
to a programmed instruction sequence.
* Menu:
* Expert:: Advanced Ibrowse commands: c, k, g, s, 1 - 9, arrows.
* Add:: Describes how to add new nodes to the hierarchy.
Also tells what nodes look like.
* Menus:: How to add to or create menus in Info nodes.
* Cross-refs:: How to add cross-references to Info nodes.
* Tags:: How to make tag tables for Info files.
* Checking:: How to check the consistency of an Info file.
* Texinfo: (texinfo).
How to generate an Info file and a printed manual
from the same source file.

File: ibrowse Node: Summary Next: Help
Ibrowse is a Python program for browsing through the Emacs Info
documentation tree. Documentation in Info is divided into "nodes",
each of which discusses one topic and contains references to other
nodes which discuss related topics. Ibrowse has commands to follow the
references and show you other nodes.
h Invoke the Ibrowse tutorial.
? Display this Summary node.
q Quit Ibrowse.
w Close current window.
Selecting other nodes:
n Move to the "next" node of this node.
p Move to the "previous" node of this node.
m Pick menu item specified by name (or abbreviation).
1-9 Pick first..ninth in node's menu.
Menu items select nodes that are "subsections" of this node.
u Move "up" from this node (i.e., from a subsection to a section).
f Follow a cross reference by name (or abbrev). Type `l' to get back.
l Move back to the last node you were in.
Moving within a node:
Space Scroll forward a full screen. DEL, BS Scroll backward.
b Go to beginning of node.
Advanced commands:
k Clone current window (create an independent duplicate).
c Copy text selection to clipboard (for paste in another application).
g Move to node specified by name.
You may include a filename as well, as (FILENAME)NODENAME.
d Go to the main directory of Info files.
t Go to Top node of this file.
s Search through this Info file for node with specified regexp.

File: ibrowse Node: Help-Small-Screen Next: Help
Since your terminal has an unusually small number of lines on its
screen, it is necessary to give you special advice at the beginning.
If you see the text "--All----" at near the bottom right corner of
the screen, it means the entire text you are looking at fits on the
screen. If you see "--Top----" instead, it means that there is more
text below that does not fit. To move forward through the text and
see another screen full, press the Space bar. To move back up, press
the key labeled Rubout or Delete or DEL.
Here are 40 lines of junk, so you can try Spaces and Rubout and
see what they do. At the end are instructions of what you should do
next.
This is line 17
This is line 18
This is line 19
This is line 20
This is line 21
This is line 22
This is line 23
This is line 24
This is line 25
This is line 26
This is line 27
This is line 28
This is line 29
This is line 30
This is line 31
This is line 32
This is line 33
This is line 34
This is line 35
This is line 36
This is line 37
This is line 38
This is line 39
This is line 40
This is line 41
This is line 42
This is line 43
This is line 44
This is line 45
This is line 46
This is line 47
This is line 48
This is line 49
This is line 50
This is line 51
This is line 52
This is line 53
This is line 54
This is line 55
This is line 56
If you have managed to get here, go back to the beginning with
Rubout, and come back here again, then you understand Space and
Rubout. So now type an "n"--just one character; don't type the
quotes and don't type a Return afterward-- to get to the normal start
of the course.

File: ibrowse Node: Help Next: Help-P Previous: Help-Small-Screen
You are talking to the program Ibrowse, for reading documentation.
Right now you are looking at one "Node" of Information.
A node contains text describing a specific topic at a specific
level of detail. This node's topic is "how to use Ibrowse".
The top line of a node is its "header". This node's header (look at
it now) says that it is the node named "Help" in the file "ibrowse".
It says that the Next node after this one is the node called "Help-P".
An advanced Ibrowse command lets you go to any node whose name you know.
Besides a "Next", a node can have a "Previous" or an "Up".
This node has a "Previous" but no "Up", as you can see.
Now it's time to move on to the Next node, named "Help-P".
>> Type "n" to move there. Type just one character;
don't type the quotes and don't type a Return afterward.
">>" in the margin means it is really time to try a command.

File: ibrowse Node: Help-P Next: Help-Page Previous: Help
This node is called "Help-P". The "Previous" node, as you see, is
"Help", which is the one you just came from using the "N" command.
Another "N" command now would take you to the Next node, "Help-Page".
>> But don't do that yet. First, try the "p" command, which takes
you to the Previous node. When you get there, you can do an "n"
again to return here.
This all probably seems insultingly simple so far, but DON'T be
led into skimming. Things will get more complicated soon. Also,
don't try a new command until you are told it's time to. Otherwise,
you may make Ibrowse skip past an important warning that was coming up.
>> Now do an "n" to get to the node "Help-Page" and learn more.

File: ibrowse Node: Help-Page Next: Help-M Previous: Help-P
Space, Backspace, and B commands.
This node's header tells you that you are now at node "Help-Page", and
that "P" would get you back to "Help-P". The line starting "Space,"
is a "Title", saying what the node is about (most nodes have titles).
This is a big node and it doesn't all fit on your display screen.
You can tell that there is more that isn't visible because you
the scroll bar on the side of the window has become active (gray).
The Space, Backspace and B commands exist to allow you to "move
around" in a node that doesn't all fit on the screen at once.
Space moves forward, to show what was below the bottom of the screen.
Backspace moves backward, to show what was above the top of the screen
(there isn't anything above the top until you have typed some spaces).
>> Now try typing a Space (afterward, type a Backspace to return here).
When you type the space, the two lines that were at the bottom of the
screen appear at the top, followed by more lines. Backspace takes the
two lines from the top and moves them to the bottom, USUALLY, but if
there are not a full screen's worth of lines above them they may not
make it all the way to the bottom.
If you type a Space when there is no more to see, it will ring the
bell and otherwise do nothing. The same goes for a Backspace when
the header of the node is visible.
Of course you can use the mouse and directly move the scroll bar
as well, but Ibrowse has keyboard commands for almost everything,
including scrolling. These keyboard commands are called "shortcuts",
because it generally takes less effort to press a key on the
keyboard than to move the mouse. On the other hand, if you are
an infrequent user of Ibrowse, you can do everything with the
mouse that you can do with the keyboard. Just look in the menus
(I'm sure you must know how to use the menus on this system, or
else you couldn't have gotten this far...). In fact you'll see that
the commands and shortcuts listed in the menus are the same as those
described in this course. You can use the shortcuts either with or
without the "Command" or "Meta" key.
Two menus are always available: the "Ibrowse" menu contains commands
pertaining to the Ibrowse program at large, while the "Navigation" menu
contains commands that move around between nodes. There may be other
menus; these will be explained later.
To move back to the beginning of the node you are on, you can type
a lot of Backspaces. You can also type simply "b" for beginning.
>> Try that now. (I have put in enough verbiage to make sure you are
not on the first screenful now). Then come back, with Spaces.
You have just learned a considerable number of commands. If you
want to use one but have trouble remembering which, just pull down
the menus to get a summary of commands and shortcuts. Some additional
shortcuts (not listed in the menus) are listed by the "Short help"
command. This brings up a dialog box which you can acknowledge
by clicking the OK button or pressing the Return key.
From now on, you will encounter large nodes without warning, and
will be expected to know how to use Space and Backspace to move
around in them without being told. Since you could change the
size of the window used, it would be impossible to warn you anyway.
>> Now type "n" to see the description of the "m" command.

File: ibrowse Node: Help-M Next: Help-Adv Previous: Help-Page
Menus and the "m" command
With only the "n" and "p" commands for moving between nodes, nodes
are restricted to a linear sequence. Menus allow a branching
structure. A menu is a list of other nodes you can move to. It is
actually just part of the text of the node formatted specially so that
Ibrowse can interpret it. The beginning of a menu is always identified
by a line which starts with "* Menu:". A node contains a menu if and
only if it has a line in it which starts that way. The only menu you
can use at any moment is the one in the node you are in. To use a
menu in any other node, you must move to that node first.
(There is an unfortunate confusion of terms here. "Menu" may refer
to one of the Ibrowse menus at the top, such as as the "Ibrowse" and
"Navigation" menus explained in the previous node, or to the menu in
a node. Where confusion is possible, these will be disambiguated by
calling them "Ibrowse menus" or "node menu".)
After the start of the menu, each line that starts with a "*"
identifies one subtopic. The line will usually contain a brief name
for the subtopic (followed by a ":"), the name of the node that talks
about that subtopic, and optionally some further description of the
subtopic. Lines in the menu that don't start with a "*" have no
special meaning - they are only for the human reader's benefit and do
not define additional subtopics. Here is an example:
* Foo: FOO's Node This tells about FOO
The subtopic name is Foo, and the node describing it is "FOO's Node".
The rest of the line is just for the reader's Information.
[[ But this line is not a real menu item, simply because there is
no line above it which starts with "* Menu:".]]
When you use a menu to go to another node (in a way that will be
described soon), what you specify is the subtopic name, the first
thing in the menu line. Ibrowse uses it to find the menu line, extracts
the node name from it, and goes to that node. The reason that there
is both a subtopic name and a node name is that the node name must be
meaningful to the computer and may therefore have to be ugly looking.
The subtopic name can be chosen just to be convenient for the user to
specify. Often the node name is convenient for the user to specify
and so both it and the subtopic name are the same. There is an
abbreviation for this:
* Foo:: This tells about FOO
This means that the subtopic name and node name are the same; they are
both "Foo".
>> Now use Spaces to find the menu in this node, then come back to
the front with a "b". As you see, a menu is actually visible
in its node. If you can't find a menu in a node by looking at it,
then the node doesn't have a menu and the "m" command is not available.
(Actually, a quicker way to see if there is a node menu, is to look
for an Ibrowse menu at the top named "Menu".)
The command to go to one of the subnodes is "m" - but DON'T DO IT
YET! Before you use "m", you must understand the difference between
commands and arguments. So far, you have learned several commands
that do not need arguments. When you type one, Ibrowse processes it and
is instantly ready for another command. The "m" command is different:
it is incomplete without the NAME OF THE SUBTOPIC. Once you have
typed "m", Ibrowse wants to read the subtopic name.
Thanks to modern user interface technology, this will be obvious:
you are prompted for the subtopic name in a dialog box. When you are
finished typing the name, press Return or click the OK button. You can
cancel the dialog box by clicking the Cancel button. The first subtopic
is provided as a default choice, so if you want to go there, you can
just press Return.
You can abbreviate the subtopic name. If the abbreviation is not
unique, the first matching subtopic is chosen. Some menus will put
the shortest possible abbreviation for each subtopic name in capital
letters, so you can see how much you need to type. It does not
matter whether you use upper case or lower case when you type the
subtopic. You should not put any spaces at the end, or inside of the
item name, except for one space where a space appears in the item in
the menu.
Here is a menu to give you a chance to practice.
* Menu: The menu starts here.
This menu gives you three ways of going to one place, Help-FOO.
* Foo: Help-FOO A node you can visit for fun
* Bar: Help-FOO Strange! two ways to get to the same place.
* Help-FOO:: And yet another!
>> Now type just an "m" and see what happens. (Read ahead before
>> trying this out, as the dialog box will probably cover these
>> instructions!)
Now you are "inside" an "m" command. Commands can't be used now;
the next thing you will type must be the name of a subtopic.
You can change your mind about doing the "m" by clicking the Cancel
button.
>> Try that now; notice the dialog box disappear.
>> Then type another "m".
>> Now type "BAR", the item name. Don't type Return yet.
While you are typing the item name, you can use the Backspace
key to cancel one character at a time if you make a mistake.
>> Type one to cancel the "R". You could type another "R" to
replace it. You don't have to, since "BA" is a valid abbreviation.
>> Now you are ready to go. Type a Return.
After visiting Help-FOO, you should return here (it will tell how).
>> Type "n" to see more commands.

File: ibrowse Node: Help-FOO Up: Help-M
The "u" command
Congratulations! This is the node Help-FOO. Unlike the other
nodes you have seen, this one has an "Up": "Help-M", the node you
just came from via the "m" command. This is the usual convention--
the nodes you reach from a menu have Ups that lead back to the menu.
Menus move Down in the tree, and Up moves Up. Previous, on the other
hand, is usually used to "stay on the same level but go backwards".
You can go back to the node Help-M by typing the command
"u" for "Up". That will put you at the FRONT of the node - to get
back to where you were reading you will have to type some Spaces.
>> Now type "u" to move back up to Help-M.

File: ibrowse Node: Help-Adv Next: Help-Q Previous: Help-M
Some advanced Ibrowse commands
The course is almost over, so please stick with it to the end.
If you have been moving around to different nodes and wish to
retrace your steps, the "l" command ("l" for "last") will do that, one
node at a time. If you have been following directions, an "l" command
now will get you back to Help-M. Another "l" command would undo the "u"
and get you back to Help-FOO. Another "l" would undo the M and get you
back to Help-M.
>> Try typing three "l"'s, pausing in between to see what each "l" does.
Then follow directions again and you will end up back here.
Note the difference between "l" and "p": "l" moves to where YOU
last were, whereas "p" always moves to the node which the header says
is the "Previous" node (from this node, to Help-M).
The "d" command gets you instantly to the Directory node.
This node, which is the first one you saw when you entered Ibrowse,
has a menu which leads (directly, or indirectly through other menus),
to all the nodes that exist.
>> Try doing a "d", then do an "l" to return here (yes, DO return).
Sometimes, in Ibrowse documentation, you will see a cross reference.
Cross references look like this: *Note Cross: Help-Cross. That is a
real, live cross reference which is named "Cross" and points at the
node named "Help-Cross".
If you wish to follow a cross reference, you must use the "f"
command. The "f" prompts for the cross reference name (in this case,
"Cross") with a dialog box.
>> Type "f", followed by "Cross", and a Return.
The "f" command allows abbreviations just like "m".
To get a list of all the cross references in the current node,
look in the Ibrowse menu at the top labeled "Footnotes". This menu is
only present if there are cross references in the current node, and
can be used to directly follow a cross reference, just like the "Menu"
menu is another way to choose an item of the node's menu.
>> Now type "n" to see the last node of the course.

File: ibrowse Node: Help-Cross
This is the node reached by the cross reference named "Cross".
While this node is specifically intended to be reached by a cross
reference, most cross references lead to nodes that "belong" someplace
else far away in the structure of Ibrowse. So you can't expect the
footnote to have a Next, Previous or Up pointing back to where you
came from. In general, the "l" (el) command is the only way to get
back there.
>> Type "l" to return to the node where the cross reference was.

File: ibrowse Node: Help-Q Previous: Help-Adv Up: Top
To get out of Ibrowse, type "q" for "Quit". All Ibrowse windows
will be closed (on UNIX, only those managed by the same process).
To close just one window, use the standard method of closing windows
on your system; you can also use "w".
This is the end of the course on using Ibrowse. There are some other
commands that are not essential or meant for experienced users; they
are useful, and you can find them by looking in the directory for
documentation on Ibrowse. Finding them will be a good exercise in using
Ibrowse in the usual manner.
>> Close this window and find back the window where you typed "h"
to enter this tutorial.
Then type "d" to go to the Ibrowse directory node if necessary,
and choose the "Ibrowse" menu item, to get to the node about
Ibrowse and see what other help is available.

File: ibrowse, Node: Expert, Up: Top, Previous: Top, Next: Add
Some Advanced Ibrowse Commands ("c", "k", "g", "s", "1" - "9", arrows).
The "c" command lets you copy text from the window to the clipboard.
You must first select the text to be copied with the mouse.
The "k" command means "klone" (we are running out of letters now...).
It creates a new Ibrowse window, showing the same node as the current.
You can then make an excursion in the new window to different nodes or
files, while the old window keeps showing the original node. Each
window has its own history for use by the "l" command.
If you know a node's name, you can go there with the "g" command.
This prompts for a node name with a dialog box. Entering, "Top"
would go to the node called Top in this file (its directory node).
Pressing "g" again and entering "Expert" would come back here.
Unlike "m", "g" does not allow the use of abbreviations.
To go to a node in another file, you can include the filename in the
node name by putting it at the front, in parentheses. Thus,
"(dir)Top" would go to the Ibrowse Directory node, which is
node Top in the file dir.
The node name "*" specifies the whole file. So you can look at all
of the current file by typing "*" or all of any other file
with "(FILENAME)*".
File names are converted to lower case before they are tried; this
is necessary to be compatible with Emacs Info. (File names are
generally relative to the Info directory, but needn't be.)
The "s" command allows you to search a whole file for a regular
expression. Unlike the corresponding Emacs Info command, it will
not search beyond the end of the current node.
Regular expressions are like in UNIX egrep; if you don't know what
regular expressions are, limit your search strings to letters, digits
and spaces. Searches in Ibrowse are case-sensitive; searching for
"foo" will not find "Foo" or "FOO"!
A description of regular expressions as they occur in Emacs is
available. (*Note Emacs Regular Expressions: (regex)syntax.)
Ibrowse regular expressions are slightly different: the meaning
of \( \| \) is swapped with that of ( | ), and there are no
escapes to handle "words" specially.
Searching starts after the current focus position. The "B" command
resets the focus to the beginning of the file, but space and backspace
leave it unchanged (so they may render the focus invisible).
If you grudge the system each character of type-in it requires,
you might like to use the commands "1", "2", "3", through "9".
They are short for the first nine entries of the node menu.
The left, right and up arrow keys are duplicates of "p", "n" and "u".
The down arrow key, as well as the Return key, goes to the first item
of the node's menu if there is one, else it executes "n". This is a
quick way to visit all nodes in a tree in pre-order: use Return to go
down and right as far as possible, then use "u" and "n" to go right
at the next higher level.

File: ibrowse, Node: Add, Up: Top, Previous: Expert, Next: Menus
To add a new topic to the list in the directory, you must
1) enter the Emacs text editor. *Note Emacs: (emacs).
2) create a node, in some file, to document that topic.
3) put that topic in the menu in the directory. *Note Menu: Menus.
The new node can live in an existing documentation file, or in a new
one. It must have a ^_ character before it (invisible to the user;
this node has one but you can't see it), and it ends with either a ^_,
or the end of file. A nice way to make a node boundary be a
page boundary as well is to put a ^L RIGHT AFTER the ^_.
The ^_ starting a node must be followed by a newline or a ^L newline,
after which comes the node's header line. The header line must give
the node's name (by which Ibrowse will find it), and state the names of
the Next, Previous, and Up nodes (if there are any). As you can see,
this node's Up node is the node Top, which points at all the
documentation for Ibrowse. The Next node is "Menus".
The keywords "Node", "Previous", "Up" and "Next", may appear in
any order, anywhere in the header line, but the recommended order is
the one in this sentence. Each keyword must be followed by a colon,
spaces and tabs, and then the appropriate name. The name may be
terminated with a tab, a comma, or a newline. A space does not end
it; node names may contain spaces. The case of letters in the names
is insignificant. "Previous" can be abbreviated to "Prev".
A node name has two forms. A node in the current file is named by
what appears after the "Node: " in that node's first line. For
example, this node's name is "Add". A node in another file is named
by "(FILENAME)NODE-WITHIN-FILE", as in "(ibrowse)Add" for this node.
If the file name is relative, it is taken starting from the standard
Info file directory of your site. The name "(FILENAME)Top" can be
abbreviated to just "(FILENAME)". By convention, the name "Top" is
used for the "highest" node in any single file - the node whose "Up"
points out of the file. The Directory node is "(dir)". The Top node
of a document file listed in the Directory should have an "Up: (dir)"
in it.
The node name "*" is special: it refers to the entire file. Thus,
g* will show you the whole current file. The use of the node * is to
make it possible to make old-fashioned, unstructured files into nodes
of the tree. Footnotes and node menus appearing in a file are disabled
when it is viewed in this way.
The "Node:" name, in which a node states its own name, must not
contain a filename, since Ibrowse when searching for a node does not
expect one to be there. The Next, Previous and Up names may contain
them. In this node, since the Up node is in the same file, it was not
necessary to use one.
Note that the nodes in this file have a File name in the header
line. The File names are ignored by Ibrowse, but they serve as
comments to help identify the node for the user.

File: ibrowse, Node: Menus, Previous: Add, Up: Top, Next: Cross-refs
How to Create Menus:
Any node in the Ibrowse hierarchy may have a MENU--a list of subnodes.
The "m" command searches the current node's menu for the topic which it
reads from the terminal.
A menu begins with a line starting with "* Menu:". The rest of the
line is a comment. After the starting line, every line that begins
with a "* " lists a single topic. The name of the topic--the arg
that the user must give to the "m" command to select this topic--
comes right after the star and space, and is followed by
a colon, spaces and tabs, and the name of the node which discusses
that topic. The node name, like node names following Next,
Previous and Up, may be terminated with a tab, comma, or newline;
it may also be terminated with a period.
If the node name and topic name are the same, than rather than
giving the name twice, the abbreviation "* NAME::" may be used
(and should be used, whenever possible, as it reduces the visual
clutter in the menu).
It is considerate to choose the topic names so that they differ
from each other very near the beginning--this allows the user to type
short abbreviations. In a long menu, it is a good idea to capitalize
the beginning of each item name which is the minimum acceptable
abbreviation for it (a long menu is more than 5 or so entries).
The node's listed in a node's menu are called its "subnodes", and
it is their "superior". They should each have an "Up:" pointing at
the superior. It is often useful to arrange all or most of the
subnodes in a sequence of Next's/Previous's so that someone who
wants to see them all need not keep revisiting the Menu.
The Info Directory is simply the menu of the node "(dir)Top"--that
is, node Top in file .../info/dir. You can put new entries in that
menu just like any other menu. The Info Directory is NOT the same as
the file directory called "info". It happens that many of Ibrowse's
files live on that file directory, but they don't have to; and files
on that directory are not automatically listed in the Info Directory
node.
The Ibrowse program uses a second directory called .../ibrowse,
which contains versions of the "dir" and "info" files adapted to
Ibrowse (the latter renamed to "ibrowse", obviously). It searches
any file first in the "ibrowse", then in the "info" directory.
(Actually, the search path is configurable.)
Also, although the Info node graph is claimed to be a "hierarchy",
in fact it can be ANY directed graph. Shared structures and pointer
cycles are perfectly possible, and can be used if they are
appropriate to the meaning to be expressed. There is no need for all
the nodes in a file to form a connected structure. In fact, this
file has two connected components. You are in one of them, which is
under the node Top; the other contains the node Help which the "h"
command goes to. In fact, since there is no garbage collector,
nothing terrible happens if a substructure is not pointed to, but
such a substructure will be rather useless since nobody will ever
find out that it exists.

File: ibrowse, Node: Cross-refs, Previous: Menus, Up: Top, Next: Tags
Creating Cross References:
A cross reference can be placed anywhere in the text, unlike a menu
item which must go at the front of a line. A cross reference looks
like a menu item except that it has "*note" instead of "*". It CANNOT
be terminated by a ")", because ")"'s are so often part of node names.
If you wish to enclose a cross reference in parentheses, terminate it
with a period first. Here are two examples of cross references pointers:
*Note details: commands. (See *note 3: Full Proof.)
They are just examples. The places they "lead to" don't really exist!

File: ibrowse, Node: Tags, Previous: Cross-refs, Up: Top, Next: Checking
Tag Tables for Info Files:
You can speed up the access to nodes of a large Info file by giving
it a tag table. Unlike the tag table for a program, the tag table for
an Info file lives inside the file itself and will automatically be
used whenever Ibrowse reads in the file.
To make a tag table, go to a node in the file using Emacs Info and type
M-x Info-tagify. Then you must use C-x C-s to save the file.
Once the Info file has a tag table, you must make certain it is up
to date. If, as a result of deletion of text, any node moves back
more than a thousand characters in the file from the position
recorded in the tag table, Ibrowse will no longer be able to find that
node. To update the tag table, use the Info-tagify command again.
An Info file tag table appears at the end of the file and looks like
this:
^_^L
Tag Table:
File: ibrowse, Node: Cross-refs21419
File: ibrowse, Node: Tags22145
^_
End Tag Table
Note that it contains one line per node, and this line contains
the beginning of the node's header (ending just after the node name),
a rubout (DEL) character, and the character position in the file of the
beginning of the node. The words "Tag Table" may occur in lower case
as well.
It is also possible for an extra level of indirection to be present.
In this case, the first line of the Tag table contains the string
"(Indirect)", and preceding the tag table is another "pseudo node"
whose header reads "Indirect:". Each following line has the form
"filename: offset", meaning that nodes at that offset or larger (but
less than the offset in the next line) really occur in the file named
here, and that the file's offset should be subtracted from the node's
offset. (Indirect tables are created by texinfo for large files.
*Note Texinfo: (texinfo). *Note Splitting files: (texinfo)Splitting.)

File: ibrowse, Node: Checking, Previous: Tags, Up: Top
Checking an Info File:
When creating an Info file, it is easy to forget the name of a node
when you are making a pointer to it from another node. If you put in
the wrong name for a node, this will not be detected until someone
tries to go through the pointer using Ibrowse. Verification of the Info
file is an automatic process which checks all pointers to nodes and
reports any pointers which are invalid. Every Next, Previous, and Up
is checked, as is every menu item and every cross reference. In addition,
any Next which doesn't have a Previous pointing back is reported.
Only pointers within the file are checked, because checking pointers
to other files would be terribly slow. But those are usually few.
To check an Info file, do M-x Info-validate while looking at any
node of the file with Emacs Info.

Tag table:
Node: Top117
Node: Summary952
Node: Help-Small-Screen997
Node: Help2628
Node: Help-P3588
Node: Help-Page4348
Node: Help-M7763
Node: Help-FOO13183
Node: Help-Adv13887
Node: Help-Cross15923
Node: Help-Q16443
Node: Expert17326
Node: Add20280
Node: Menus23273
Node: Cross-refs26394
Node: Tags27050
Node: Checking28966

End tag table

View File

@ -1,617 +0,0 @@
# Browser for "Info files" as used by the Emacs documentation system.
#
# Now you can read Info files even if you can't spare the memory, time or
# disk space to run Emacs. (I have used this extensively on a Macintosh
# with 1 Megabyte main memory and a 20 Meg harddisk.)
#
# You can give this to someone with great fear of complex computer
# systems, as long as they can use a mouse.
#
# Another reason to use this is to encourage the use of Info for on-line
# documentation of software that is not related to Emacs or GNU.
# (In particular, I plan to redo the Python and STDWIN documentation
# in texinfo.)
# NB: this is not a self-executing script. You must startup Python,
# import ibrowse, and call ibrowse.main(). On UNIX, the script 'ib'
# runs the browser.
# Configuration:
#
# - The pathname of the directory (or directories) containing
# the standard Info files should be set by editing the
# value assigned to INFOPATH in module ifile.py.
#
# - The default font should be set by editing the value of FONT
# in this module (ibrowse.py).
#
# - For fastest I/O, you may look at BLOCKSIZE and a few other
# constants in ifile.py.
# This is a fairly large Python program, split in the following modules:
#
# ibrowse.py Main program and user interface.
# This is the only module that imports stdwin.
#
# ifile.py This module knows about the format of Info files.
# It is imported by all of the others.
#
# itags.py This module knows how to read prebuilt tag tables,
# including indirect ones used by large texinfo files.
#
# icache.py Caches tag tables and visited nodes.
# XXX There should really be a different tutorial, as the user interface
# XXX differs considerably from Emacs...
import sys
import regexp
import stdwin
from stdwinevents import *
import string
from ifile import NoSuchFile, NoSuchNode
import icache
# Default font.
# This should be an acceptable argument for stdwin.setfont();
# on the Mac, this can be a pair (fontname, pointsize), while
# under X11 it should be a standard X11 font name.
# For best results, use a constant width font like Courier;
# many Info files contain tabs that don't align with other text
# unless all characters have the same width.
#
#FONT = ('Monaco', 9) # Mac
FONT = '-schumacher-clean-medium-r-normal--14-140-75-75-c-70-iso8859-1' # X11
# Try not to destroy the list of windows when reload() is used.
# This is useful during debugging, and harmless in production...
#
try:
dummy = windows
del dummy
except NameError:
windows = []
# Default main function -- start at the '(dir)' node.
#
def main():
start('(dir)')
# Start at an arbitrary node.
# The default file is 'ibrowse'.
#
def start(ref):
stdwin.setdefscrollbars(0, 1)
stdwin.setfont(FONT)
stdwin.setdefwinsize(76*stdwin.textwidth('x'), 22*stdwin.lineheight())
makewindow('ibrowse', ref)
mainloop()
# Open a new browser window.
# Arguments specify the default file and a node reference
# (if the node reference specifies a file, the default file is ignored).
#
def makewindow(file, ref):
win = stdwin.open('Info file Browser, by Guido van Rossum')
win.mainmenu = makemainmenu(win)
win.navimenu = makenavimenu(win)
win.textobj = win.textcreate((0, 0), win.getwinsize())
win.file = file
win.node = ''
win.last = []
win.pat = ''
win.dispatch = idispatch
win.nodemenu = None
win.footmenu = None
windows.append(win)
imove(win, ref)
# Create the 'Ibrowse' menu for a new browser window.
#
def makemainmenu(win):
mp = win.menucreate('Ibrowse')
mp.callback = []
additem(mp, 'New window (clone)', 'K', iclone)
additem(mp, 'Help (tutorial)', 'H', itutor)
additem(mp, 'Command summary', '?', isummary)
additem(mp, 'Close this window', 'W', iclose)
additem(mp, '', '', None)
additem(mp, 'Copy to clipboard', 'C', icopy)
additem(mp, '', '', None)
additem(mp, 'Search regexp...', 'S', isearch)
additem(mp, '', '', None)
additem(mp, 'Reset node cache', '', iresetnodecache)
additem(mp, 'Reset entire cache', '', iresetcache)
additem(mp, '', '', None)
additem(mp, 'Quit', 'Q', iquit)
return mp
# Create the 'Navigation' menu for a new browser window.
#
def makenavimenu(win):
mp = win.menucreate('Navigation')
mp.callback = []
additem(mp, 'Menu item...', 'M', imenu)
additem(mp, 'Follow reference...', 'F', ifollow)
additem(mp, 'Go to node...', 'G', igoto)
additem(mp, '', '', None)
additem(mp, 'Next node in tree', 'N', inext)
additem(mp, 'Previous node in tree', 'P', iprev)
additem(mp, 'Up in tree', 'U', iup)
additem(mp, 'Last visited node', 'L', ilast)
additem(mp, 'Top of tree', 'T', itop)
additem(mp, 'Directory node', 'D', idir)
return mp
# Add an item to a menu, and a function to its list of callbacks.
# (Specifying all in one call is the only way to keep the menu
# and the list of callbacks in synchrony.)
#
def additem(mp, text, shortcut, function):
if shortcut:
mp.additem(text, shortcut)
else:
mp.additem(text)
mp.callback.append(function)
# Stdwin event processing main loop.
# Return when there are no windows left.
# Note that windows not in the windows list don't get their events.
#
def mainloop():
while windows:
event = stdwin.getevent()
if event[1] in windows:
try:
event[1].dispatch(event)
except KeyboardInterrupt:
# The user can type Control-C (or whatever)
# to leave the browser without closing
# the window. Mainly useful for
# debugging.
break
except:
# During debugging, it was annoying if
# every mistake in a callback caused the
# whole browser to crash, hence this
# handler. In a production version
# it may be better to disable this.
#
msg = sys.exc_type
if sys.exc_value:
val = sys.exc_value
if type(val) <> type(''):
val = `val`
msg = msg + ': ' + val
msg = 'Oops, an exception occurred: ' + msg
event = None
stdwin.message(msg)
event = None
# Handle one event. The window is taken from the event's window item.
# This function is placed as a method (named 'dispatch') on the window,
# so the main loop will be able to handle windows of a different kind
# as well, as long as they are all placed in the list of windows.
#
def idispatch(event):
type, win, detail = event
if type == WE_CHAR:
if not keybindings.has_key(detail):
detail = string.lower(detail)
if keybindings.has_key(detail):
keybindings[detail](win)
return
if detail in '0123456789':
i = eval(detail) - 1
if i < 0: i = len(win.menu) + i
if 0 <= i < len(win.menu):
topic, ref = win.menu[i]
imove(win, ref)
return
stdwin.fleep()
return
if type == WE_COMMAND:
if detail == WC_LEFT:
iprev(win)
elif detail == WC_RIGHT:
inext(win)
elif detail == WC_UP:
iup(win)
elif detail == WC_DOWN:
idown(win)
elif detail == WC_BACKSPACE:
ibackward(win)
elif detail == WC_RETURN:
idown(win)
else:
stdwin.fleep()
return
if type == WE_MENU:
mp, item = detail
if mp == None:
pass # A THINK C console menu was selected
elif mp in (win.mainmenu, win.navimenu):
mp.callback[item](win)
elif mp == win.nodemenu:
topic, ref = win.menu[item]
imove(win, ref)
elif mp == win.footmenu:
topic, ref = win.footnotes[item]
imove(win, ref)
return
if type == WE_SIZE:
win.textobj.move((0, 0), win.getwinsize())
(left, top), (right, bottom) = win.textobj.getrect()
win.setdocsize(0, bottom)
return
if type == WE_CLOSE:
iclose(win)
return
if not win.textobj.event(event):
pass
# Paging callbacks
def ibeginning(win):
win.setorigin(0, 0)
win.textobj.setfocus(0, 0) # To restart searches
def iforward(win):
lh = stdwin.lineheight() # XXX Should really use the window's...
h, v = win.getorigin()
docwidth, docheight = win.getdocsize()
width, height = win.getwinsize()
if v + height >= docheight:
stdwin.fleep()
return
increment = max(lh, ((height - 2*lh) / lh) * lh)
v = v + increment
win.setorigin(h, v)
def ibackward(win):
lh = stdwin.lineheight() # XXX Should really use the window's...
h, v = win.getorigin()
if v <= 0:
stdwin.fleep()
return
width, height = win.getwinsize()
increment = max(lh, ((height - 2*lh) / lh) * lh)
v = max(0, v - increment)
win.setorigin(h, v)
# Ibrowse menu callbacks
def iclone(win):
stdwin.setdefwinsize(win.getwinsize())
makewindow(win.file, win.node)
def itutor(win):
# The course looks best at 76x22...
stdwin.setdefwinsize(76*stdwin.textwidth('x'), 22*stdwin.lineheight())
makewindow('ibrowse', 'Help')
def isummary(win):
stdwin.setdefwinsize(76*stdwin.textwidth('x'), 22*stdwin.lineheight())
makewindow('ibrowse', 'Summary')
def iclose(win):
#
# Remove the window from the windows list so the mainloop
# will notice if all windows are gone.
# Delete the textobj since it constitutes a circular reference
# to the window which would prevent it from being closed.
# (Deletion is done by assigning None to avoid crashes
# when closing a half-initialized window.)
#
if win in windows:
windows.remove(win)
win.textobj = None
def icopy(win):
focustext = win.textobj.getfocustext()
if not focustext:
stdwin.fleep()
else:
stdwin.rotatecutbuffers(1)
stdwin.setcutbuffer(0, focustext)
# XXX Should also set the primary selection...
def isearch(win):
try:
pat = stdwin.askstr('Search pattern:', win.pat)
except KeyboardInterrupt:
return
if not pat:
pat = win.pat
if not pat:
stdwin.message('No previous pattern')
return
try:
cpat = regexp.compile(pat)
except regexp.error, msg:
stdwin.message('Bad pattern: ' + msg)
return
win.pat = pat
f1, f2 = win.textobj.getfocus()
text = win.text
match = cpat.match(text, f2)
if not match:
stdwin.fleep()
return
a, b = match[0]
win.textobj.setfocus(a, b)
def iresetnodecache(win):
icache.resetnodecache()
def iresetcache(win):
icache.resetcache()
def iquit(win):
for win in windows[:]:
iclose(win)
# Navigation menu callbacks
def imenu(win):
ichoice(win, 'Menu item (abbreviated):', win.menu, whichmenuitem(win))
def ifollow(win):
ichoice(win, 'Follow reference named (abbreviated):', \
win.footnotes, whichfootnote(win))
def igoto(win):
try:
choice = stdwin.askstr('Go to node (full name):', '')
except KeyboardInterrupt:
return
if not choice:
stdwin.message('Sorry, Go to has no default')
return
imove(win, choice)
def inext(win):
prev, next, up = win.header
if next:
imove(win, next)
else:
stdwin.fleep()
def iprev(win):
prev, next, up = win.header
if prev:
imove(win, prev)
else:
stdwin.fleep()
def iup(win):
prev, next, up = win.header
if up:
imove(win, up)
else:
stdwin.fleep()
def ilast(win):
if not win.last:
stdwin.fleep()
else:
i = len(win.last)-1
lastnode, lastfocus = win.last[i]
imove(win, lastnode)
if len(win.last) > i+1:
# The move succeeded -- restore the focus
win.textobj.setfocus(lastfocus)
# Delete the stack top even if the move failed,
# else the whole stack would remain unreachable
del win.last[i:] # Delete the entry pushed by imove as well!
def itop(win):
imove(win, '')
def idir(win):
imove(win, '(dir)')
# Special and generic callbacks
def idown(win):
if win.menu:
default = whichmenuitem(win)
for topic, ref in win.menu:
if default == topic:
break
else:
topic, ref = win.menu[0]
imove(win, ref)
else:
inext(win)
def ichoice(win, prompt, list, default):
if not list:
stdwin.fleep()
return
if not default:
topic, ref = list[0]
default = topic
try:
choice = stdwin.askstr(prompt, default)
except KeyboardInterrupt:
return
if not choice:
return
choice = string.lower(choice)
n = len(choice)
for topic, ref in list:
topic = string.lower(topic)
if topic[:n] == choice:
imove(win, ref)
return
stdwin.message('Sorry, no topic matches ' + `choice`)
# Follow a reference, in the same window.
#
def imove(win, ref):
savetitle = win.gettitle()
win.settitle('Looking for ' + ref + '...')
#
try:
file, node, header, menu, footnotes, text = \
icache.get_node(win.file, ref)
except NoSuchFile, file:
win.settitle(savetitle)
stdwin.message(\
'Sorry, I can\'t find a file named ' + `file` + '.')
return
except NoSuchNode, node:
win.settitle(savetitle)
stdwin.message(\
'Sorry, I can\'t find a node named ' + `node` + '.')
return
#
win.settitle('Found (' + file + ')' + node + '...')
#
if win.file and win.node:
lastnode = '(' + win.file + ')' + win.node
win.last.append((lastnode, win.textobj.getfocus()))
win.file = file
win.node = node
win.header = header
win.menu = menu
win.footnotes = footnotes
win.text = text
#
win.setorigin(0, 0) # Scroll to the beginnning
win.textobj.settext(text)
win.textobj.setfocus(0, 0)
(left, top), (right, bottom) = win.textobj.getrect()
win.setdocsize(0, bottom)
#
if win.footmenu: win.footmenu.close()
if win.nodemenu: win.nodemenu.close()
win.footmenu = None
win.nodemenu = None
#
win.menu = menu
if menu:
win.nodemenu = win.menucreate('Menu')
digit = 1
for topic, ref in menu:
if digit < 10:
win.nodemenu.additem(topic, `digit`)
else:
win.nodemenu.additem(topic)
digit = digit + 1
#
win.footnotes = footnotes
if footnotes:
win.footmenu = win.menucreate('Footnotes')
for topic, ref in footnotes:
win.footmenu.additem(topic)
#
win.settitle('(' + win.file + ')' + win.node)
# Find menu item at focus
#
findmenu = regexp.compile('^\* [mM]enu:').match
findmenuitem = regexp.compile( \
'^\* ([^:]+):[ \t]*(:|\([^\t]*\)[^\t,\n.]*|[^:(][^\t,\n.]*)').match
#
def whichmenuitem(win):
if not win.menu:
return ''
match = findmenu(win.text)
if not match:
return ''
a, b = match[0]
i = b
f1, f2 = win.textobj.getfocus()
lastmatch = ''
while i < len(win.text):
match = findmenuitem(win.text, i)
if not match:
break
(a, b), (a1, b1), (a2, b2) = match
if a > f1:
break
lastmatch = win.text[a1:b1]
i = b
return lastmatch
# Find footnote at focus
#
findfootnote = \
regexp.compile('\*[nN]ote ([^:]+):[ \t]*(:|[^:][^\t,\n.]*)').match
#
def whichfootnote(win):
if not win.footnotes:
return ''
i = 0
f1, f2 = win.textobj.getfocus()
lastmatch = ''
while i < len(win.text):
match = findfootnote(win.text, i)
if not match:
break
(a, b), (a1, b1), (a2, b2) = match
if a > f1:
break
lastmatch = win.text[a1:b1]
i = b
return lastmatch
# Now all the "methods" are defined, we can initialize the table
# of key bindings.
#
keybindings = {}
# Window commands
keybindings['k'] = iclone
keybindings['h'] = itutor
keybindings['?'] = isummary
keybindings['w'] = iclose
keybindings['c'] = icopy
keybindings['s'] = isearch
keybindings['q'] = iquit
# Navigation commands
keybindings['m'] = imenu
keybindings['f'] = ifollow
keybindings['g'] = igoto
keybindings['n'] = inext
keybindings['p'] = iprev
keybindings['u'] = iup
keybindings['l'] = ilast
keybindings['d'] = idir
keybindings['t'] = itop
# Paging commands
keybindings['b'] = ibeginning
keybindings['.'] = ibeginning
keybindings[' '] = iforward

View File

@ -1,74 +0,0 @@
# Cache management for info file processing.
# The function get_node() is the standard interface;
# its signature is the same as ifile.get_node() but it uses
# the cache and supports indirect tag tables.
import string
import ifile
from ifile import NoSuchNode, NoSuchFile
import itags
# Special hack to save the cache when using reload().
# This can just be "cache = {}" in a production version.
#
try:
dummy = cache
del dummy
except NameError:
cache = {}
# Clear the entire cache.
#
def resetcache():
for key in cache.keys():
del cache[key]
# Clear the node info from the cache (the most voluminous data).
#
def resetnodecache():
for key in cache.keys():
tags, nodes = cache[key]
cache[key] = tags, {}
# Get a node.
#
def get_node(curfile, ref):
file, node = ifile.parse_ref(curfile, ref)
file = string.lower(file)
node = string.lower(node)
if node == '*':
# Don't cache whole file references;
# reading the data is faster than displaying it anyway.
return ifile.get_whole_file(file) # May raise NoSuchFile
if not cache.has_key(file):
cache[file] = get_tags(file), {} # May raise NoSuchFile
tags, nodes = cache[file]
if not nodes.has_key(node):
if not tags.has_key(node):
raise NoSuchNode, ref
file1, offset, line = tags[node]
if not file1:
file1 = file
file1, node1, header, menu, footnotes, text = \
ifile.get_file_node(file1, offset, node)
nodes[node] = file, node1, header, menu, footnotes, text
return nodes[node]
# Get the tag table for a file.
# Either construct one or get the one found in the file.
# Raise NoSuchFile if the file isn't found.
#
def get_tags(file):
f = ifile.try_open(file) # May raise NoSuchFile
tags = itags.get_tags(f)
if not tags:
###print 'Scanning file...'
f.seek(0)
tags = ifile.make_tags(f)
return tags

View File

@ -1,328 +0,0 @@
# Tools for info file processing.
# XXX Need to be more careful with reading ahead searching for nodes.
import regexp
import string
# Exported exceptions.
#
NoSuchFile = 'no such file'
NoSuchNode = 'no such node'
# The search path for info files; this is site-specific.
# Directory names should end in a partname delimiter,
# so they can simply be concatenated to a relative pathname.
#
#INFOPATH = ['', ':Info.Ibrowse:', ':Info:'] # Mac
INFOPATH = ['', '/usr/local/emacs/info/'] # X11 on UNIX
# Tunable constants.
#
BLOCKSIZE = 512 # Qty to align reads to, if possible
FUZZ = 2*BLOCKSIZE # Qty to back-up before searching for a node
CHUNKSIZE = 4*BLOCKSIZE # Qty to read at once when reading lots of data
# Regular expressions used.
# Note that it is essential that Python leaves unrecognized backslash
# escapes in a string so they can be seen by regexp.compile!
#
findheader = regexp.compile('\037\014?\n(.*\n)').match
findescape = regexp.compile('\037').match
parseheader = regexp.compile('[nN]ode:[ \t]*([^\t,\n]*)').match
findfirstline = regexp.compile('^.*\n').match
findnode = regexp.compile('[nN]ode:[ \t]*([^\t,\n]*)').match
findprev = regexp.compile('[pP]rev[ious]*:[ \t]*([^\t,\n]*)').match
findnext = regexp.compile('[nN]ext:[ \t]*([^\t,\n]*)').match
findup = regexp.compile('[uU]p:[ \t]*([^\t,\n]*)').match
findmenu = regexp.compile('^\* [mM]enu:').match
findmenuitem = regexp.compile( \
'^\* ([^:]+):[ \t]*(:|\([^\t]*\)[^\t,\n.]*|[^:(][^\t,\n.]*)').match
findfootnote = regexp.compile( \
'\*[nN]ote ([^:]+):[ \t]*(:|[^:][^\t,\n.]*)').match
parsenoderef = regexp.compile('^\((.*)\)(.*)$').match
# Get a node and all information pertaining to it.
# This doesn't work if there is an indirect tag table,
# and in general you are better off using icache.get_node() instead.
# Functions get_whole_file() and get_file_node() provide part
# functionality used by icache.
# Raise NoSuchFile or NoSuchNode as appropriate.
#
def get_node(curfile, ref):
file, node = parse_ref(curfile, ref)
if node == '*':
return get_whole_file(file)
else:
return get_file_node(file, 0, node)
#
def get_whole_file(file):
f = try_open(file) # May raise NoSuchFile
text = f.read()
header, menu, footnotes = ('', '', ''), [], []
return file, '*', header, menu, footnotes, text
#
def get_file_node(file, offset, node):
f = try_open(file) # May raise NoSuchFile
text = find_node(f, offset, node) # May raise NoSuchNode
node, header, menu, footnotes = analyze_node(text)
return file, node, header, menu, footnotes, text
# Parse a node reference into a file (possibly default) and node name.
# Possible reference formats are: "NODE", "(FILE)", "(FILE)NODE".
# Default file is the curfile argument; default node is Top.
# A node value of '*' is a special case: the whole file should
# be interpreted (by the caller!) as a single node.
#
def parse_ref(curfile, ref):
match = parsenoderef(ref)
if not match:
file, node = curfile, ref
else:
(a, b), (a1, b1), (a2, b2) = match
file, node = ref[a1:b1], ref[a2:b2]
if not file:
file = curfile # (Is this necessary?)
if not node:
node = 'Top'
return file, node
# Extract node name, links, menu and footnotes from the node text.
#
def analyze_node(text):
#
# Get node name and links from the header line
#
match = findfirstline(text)
if match:
(a, b) = match[0]
line = text[a:b]
else:
line = ''
node = get_it(text, findnode)
prev = get_it(text, findprev)
next = get_it(text, findnext)
up = get_it(text, findup)
#
# Get the menu items, if there is a menu
#
menu = []
match = findmenu(text)
if match:
(a, b) = match[0]
while 1:
match = findmenuitem(text, b)
if not match:
break
(a, b), (a1, b1), (a2, b2) = match
topic, ref = text[a1:b1], text[a2:b2]
if ref == ':':
ref = topic
menu.append((topic, ref))
#
# Get the footnotes
#
footnotes = []
b = 0
while 1:
match = findfootnote(text, b)
if not match:
break
(a, b), (a1, b1), (a2, b2) = match
topic, ref = text[a1:b1], text[a2:b2]
if ref == ':':
ref = topic
footnotes.append((topic, ref))
#
return node, (prev, next, up), menu, footnotes
#
def get_it(line, matcher):
match = matcher(line)
if not match:
return ''
else:
(a, b), (a1, b1) = match
return line[a1:b1]
# Find a node in an open file.
# The offset (from the tags table) is a hint about the node's position.
# Pass zero if there is no tags table.
# Raise NoSuchNode if the node isn't found.
# NB: This seeks around in the file.
#
def find_node(f, offset, node):
node = string.lower(node) # Just to be sure
#
# Position a little before the given offset,
# so we may find the node even if it has moved around
# in the file a little.
#
offset = max(0, ((offset-FUZZ) / BLOCKSIZE) * BLOCKSIZE)
f.seek(offset)
#
# Loop, hunting for a matching node header.
#
while 1:
buf = f.read(CHUNKSIZE)
if not buf:
break
i = 0
while 1:
match = findheader(buf, i)
if match:
(a,b), (a1,b1) = match
start = a1
line = buf[a1:b1]
i = b
match = parseheader(line)
if match:
(a,b), (a1,b1) = match
key = string.lower(line[a1:b1])
if key == node:
# Got it! Now read the rest.
return read_node(f, buf[start:])
elif findescape(buf, i):
next = f.read(CHUNKSIZE)
if not next:
break
buf = buf + next
else:
break
#
# If we get here, we didn't find it. Too bad.
#
raise NoSuchNode, node
# Finish off getting a node (subroutine for find_node()).
# The node begins at the start of buf and may end in buf;
# if it doesn't end there, read additional data from f.
#
def read_node(f, buf):
i = 0
match = findescape(buf, i)
while not match:
next = f.read(CHUNKSIZE)
if not next:
end = len(buf)
break
i = len(buf)
buf = buf + next
match = findescape(buf, i)
else:
# Got a match
(a, b) = match[0]
end = a
# Strip trailing newlines
while end > 0 and buf[end-1] == '\n':
end = end-1
buf = buf[:end]
return buf
# Read reverse starting at offset until the beginning of a node is found.
# Then return a buffer containing the beginning of the node,
# with f positioned just after the buffer.
# The buffer will contain at least the full header line of the node;
# the caller should finish off with read_node() if it is the right node.
# (It is also possible that the buffer extends beyond the node!)
# Return an empty string if there is no node before the given offset.
#
def backup_node(f, offset):
start = max(0, ((offset-CHUNKSIZE) / BLOCKSIZE) * BLOCKSIZE)
end = offset
while start < end:
f.seek(start)
buf = f.read(end-start)
i = 0
hit = -1
while 1:
match = findheader(buf, i)
if match:
(a,b), (a1,b1) = match
hit = a1
i = b
elif end < offset and findescape(buf, i):
next = f.read(min(offset-end, BLOCKSIZE))
if not next:
break
buf = buf + next
end = end + len(next)
else:
break
if hit >= 0:
return buf[hit:]
end = start
start = max(0, end - CHUNKSIZE)
return ''
# Make a tag table for the given file by scanning the file.
# The file must be open for reading, and positioned at the beginning
# (or wherever the hunt for tags must begin; it is read till the end).
#
def make_tags(f):
tags = {}
while 1:
offset = f.tell()
buf = f.read(CHUNKSIZE)
if not buf:
break
i = 0
while 1:
match = findheader(buf, i)
if match:
(a,b), (a1,b1) = match
start = offset+a1
line = buf[a1:b1]
i = b
match = parseheader(line)
if match:
(a,b), (a1,b1) = match
key = string.lower(line[a1:b1])
if tags.has_key(key):
print 'Duplicate node:',
print key
tags[key] = '', start, line
elif findescape(buf, i):
next = f.read(CHUNKSIZE)
if not next:
break
buf = buf + next
else:
break
return tags
# Try to open a file, return a file object if succeeds.
# Raise NoSuchFile if the file can't be opened.
# Should treat absolute pathnames special.
#
def try_open(file):
for dir in INFOPATH:
try:
return open(dir + file, 'r')
except IOError:
pass
raise NoSuchFile, file
# A little test for the speed of make_tags().
#
TESTFILE = 'texinfo-1'
def test_make_tags():
import time
f = try_open(TESTFILE)
t1 = time.millitimer()
tags = make_tags(f)
t2 = time.millitimer()
print 'Making tag table for', `TESTFILE`, 'took', t2-t1, 'msec.'

View File

@ -1,127 +0,0 @@
# Utility module for 'icache.py': interpret tag tables and indirect nodes.
# (This module is a bit chatty when confronted with the unexpected.)
import regexp
import string
import ifile
# Get the tag table of an open file, as a dictionary.
# Seeks around in the file; after reading, the position is undefined.
# Return an empty tag table if none is found.
#
def get_tags(f):
#
# First see if the last "node" is the end of tag table marker.
#
f.seek(0, 2) # Seek to EOF
end = f.tell()
buf = ifile.backup_node(f, end)
if not labelmatch(buf, 0, 'end tag table\n'):
return {} # No succes
#
# Next backup to the previous "node" -- the tag table itself.
#
###print 'Getting prebuilt tag table...'
end = f.tell() - len(buf)
buf = ifile.backup_node(f, end)
label = 'tag table:\n'
if not labelmatch(buf, 0, label):
print 'Weird: end tag table marker but no tag table?'
print 'Node begins:', `buf[:50]`
return {}
#
# Now read the whole tag table.
#
end = f.tell() - len(buf) # Do this first!
buf = ifile.read_node(f, buf)
#
# First check for an indirection table.
#
indirlist = []
if labelmatch(buf, len(label), '(indirect)\n'):
indirbuf = ifile.backup_node(f, end)
if not labelmatch(indirbuf, 0, 'indirect:\n'):
print 'Weird: promised indirection table not found'
print 'Node begins:', `indirbuf[:50]`
# Carry on. Things probably won't work though.
else:
indirbuf = ifile.read_node(f, indirbuf)
indirlist = parse_indirlist(indirbuf)
#
# Now parse the tag table.
#
findtag = regexp.compile('^(.*[nN]ode:[ \t]*(.*))\177([0-9]+)$').match
i = 0
tags = {}
while 1:
match = findtag(buf, i)
if not match:
break
(a,b), (a1,b1), (a2,b2), (a3,b3) = match
i = b
line = buf[a1:b1]
node = string.lower(buf[a2:b2])
offset = eval(buf[a3:b3]) # XXX What if it overflows?
if tags.has_key(node):
print 'Duplicate key in tag table:', `node`
file, offset = map_offset(offset, indirlist)
tags[node] = file, offset, line
#
return tags
# Return true if buf[i:] begins with a label, after lower case conversion.
# The label argument must be in lower case.
#
def labelmatch(buf, i, label):
return string.lower(buf[i:i+len(label)]) == label
# Parse the indirection list.
# Return a list of (filename, offset) pairs ready for use.
#
def parse_indirlist(buf):
list = []
findindir = regexp.compile('^(.+):[ \t]*([0-9]+)$').match
i = 0
while 1:
match = findindir(buf, i)
if not match:
break
(a,b), (a1,b1), (a2,b2) = match
file = buf[a1:b1]
offset = eval(buf[a2:b2]) # XXX What if this gets overflow?
list.append((file, offset))
i = b
return list
# Map an offset through the indirection list.
# Return (filename, new_offset).
# If the list is empty, return the given offset and an empty file name.
#
def map_offset(offset, indirlist):
if not indirlist:
return '', offset
#
# XXX This could be done more elegant.
#
filex, offx = indirlist[0]
for i in range(len(indirlist)):
file1, off1 = indirlist[i]
if i+1 >= len(indirlist):
file2, off2 = '', 0x7fffffff
else:
file2, off2 = indirlist[i+1]
if off1 <= offset < off2:
# Add offx+2 to compensate for extra header.
# No idea whether this is always correct.
return file1, offset-off1 + offx+2
#
# XXX Shouldn't get here.
#
print 'Oops, map_offset fell through'
return '', offset # Not likely to get good results

View File

@ -1,413 +0,0 @@
#! /usr/bin/env python
# XXX This only works on SGIs running IRIX 4.0 or higher
# JUKEBOX: browse directories full of sampled sound files.
#
# One or more "list windows" display the files and subdirectories of
# the arguments. Double-clicking on a subdirectory opens a new window
# displaying its contents (and so on recursively). Double clicking
# on a file plays it as a sound file (assuming it is one).
#
# Playing is asynchronous: the application keeps listening for events
# while the sample is playing, so you can cancel playing or start a
# new sample right away. Synchronous playing is available through the
# -s option.
#
# The control window displays a "stop button" that cancel the current
# play request.
#
# Most sound file formats recognized by SOX or SFPLAY are recognized.
# Since conversion is costly, converted files are cached in
# /usr/tmp/@j* until the user quits or changes the sampling rate via
# the Rate menu.
import commands
import getopt
import os
from stat import *
import rand
import stdwin
from stdwinevents import *
import sys
import tempfile
import sndhdr
from WindowParent import WindowParent
from Buttons import PushButton
# Pathnames
DEF_DB = '/usr/local/sounds' # Default directory of sounds
SOX = '/usr/local/bin/sox' # Sound format conversion program
SFPLAY = '/usr/sbin/sfplay' # Sound playing program
# Global variables
class struct: pass # Class to define featureless structures
G = struct() # Holds writable global variables
# Main program
def main():
G.synchronous = 0 # If set, use synchronous audio.write()
G.debug = 0 # If set, print debug messages
G.busy = 0 # Set while asynchronous playing is active
G.windows = [] # List of open windows, except control
G.mode = '' # File type (default any that sfplay knows)
G.rate = 0 # Sampling rate (default " " " ")
G.tempprefix = tempfile.mktemp()
#
try:
optlist, args = getopt.getopt(sys.argv[1:], 'dr:st:')
except getopt.error, msg:
sys.stdout = sys.stderr
print msg
print 'usage: jukebox [-d] [-s] [-t type] [-r rate]'
print ' -d debugging (-dd event debugging)'
print ' -s synchronous playing'
print ' -t type file type'
print ' -r rate sampling rate'
sys.exit(2)
#
for optname, optarg in optlist:
if optname == '-d':
G.debug = G.debug + 1
elif optname == '-r':
G.rate = int(eval(optarg))
elif optname == '-s':
G.synchronous = 1
elif optname == '-t':
G.mode = optarg
#
if G.debug:
for name in G.__dict__.keys():
print 'G.' + name, '=', `G.__dict__[name]`
#
if not args:
args = [DEF_DB]
#
G.cw = opencontrolwindow()
for dirname in args:
G.windows.append(openlistwindow(dirname))
#
#
try:
maineventloop()
finally:
clearcache()
killchild()
# Entries in Rate menu:
rates = ['default', '7350', \
'8000', '11025', '16000', '22050', '32000', '41000', '48000']
def maineventloop():
mouse_events = WE_MOUSE_DOWN, WE_MOUSE_MOVE, WE_MOUSE_UP
while G.windows:
try:
type, w, detail = event = stdwin.getevent()
except KeyboardInterrupt:
killchild()
continue
if w == G.cw.win:
if type == WE_CLOSE:
return
if type == WE_TIMER:
checkchild()
if G.busy:
G.cw.win.settimer(1)
elif type == WE_MENU:
menu, item = detail
if menu is G.ratemenu:
clearcache()
if item == 0:
G.rate = 0
else:
G.rate = eval(rates[item])
for i in range(len(rates)):
menu.check(i, (i == item))
else:
G.cw.dispatch(event)
else:
if type == WE_DRAW:
w.drawproc(w, detail)
elif type in mouse_events:
w.mouse(w, type, detail)
elif type == WE_CLOSE:
w.close(w)
del w, event
else:
if G.debug > 1: print type, w, detail
def checkchild():
if G.busy:
waitchild(1)
def killchild():
if G.busy:
os.kill(G.busy, 9)
waitchild(0)
def waitchild(options):
pid, sts = os.waitpid(G.busy, options)
if pid == G.busy:
G.busy = 0
G.stop.enable(0)
# Control window -- to set gain and cancel play operations in progress
def opencontrolwindow():
stdwin.setdefscrollbars(0, 0)
cw = WindowParent().create('Jukebox', (0, 0))
#
stop = PushButton().definetext(cw, ' Stop ')
stop.hook = stop_hook
stop.enable(0)
G.stop = stop
#
cw.realize()
#
G.ratemenu = cw.win.menucreate('Rate')
for r in rates:
G.ratemenu.additem(r)
if G.rate == 0:
G.ratemenu.check(0, 1)
else:
for i in len(range(rates)):
if rates[i] == `G.rate`:
G.ratemenu.check(i, 1)
#
return cw
def stop_hook(self):
killchild()
# List windows -- to display list of files and subdirectories
def openlistwindow(dirname):
list = os.listdir(dirname)
list.sort()
i = 0
while i < len(list):
if list[i][0] == '.':
del list[i]
else:
i = i+1
for i in range(len(list)):
fullname = os.path.join(dirname, list[i])
if os.path.isdir(fullname):
info = '/'
else:
try:
size = os.stat(fullname)[ST_SIZE]
info = `(size + 1023)/1024` + 'k'
except IOError:
info = '???'
info = '(' + info + ')'
list[i] = list[i], info
width = maxwidth(list)
# width = width + stdwin.textwidth(' ') # XXX X11 stdwin bug workaround
height = len(list) * stdwin.lineheight()
stdwin.setdefwinsize(width, min(height, 500))
stdwin.setdefscrollbars(0, 1)
w = stdwin.open(dirname)
stdwin.setdefwinsize(0, 0)
w.setdocsize(width, height)
w.drawproc = drawlistwindow
w.mouse = mouselistwindow
w.close = closelistwindow
w.dirname = dirname
w.list = list
w.selected = -1
return w
def maxwidth(list):
width = 1
for name, info in list:
w = stdwin.textwidth(name + ' ' + info)
if w > width: width = w
return width
def drawlistwindow(w, area):
## (left, top), (right, bottom) = area
d = w.begindrawing()
d.erase((0, 0), (1000, 10000))
lh = d.lineheight()
h, v = 0, 0
for name, info in w.list:
if info == '/':
text = name + '/'
else:
text = name + ' ' + info
d.text((h, v), text)
v = v + lh
showselection(w, d)
d.close()
def hideselection(w, d):
if w.selected >= 0:
invertselection(w, d)
def showselection(w, d):
if w.selected >= 0:
invertselection(w, d)
def invertselection(w, d):
lh = d.lineheight()
h1, v1 = p1 = 0, w.selected*lh
h2, v2 = p2 = 1000, v1 + lh
d.invert(p1, p2)
def mouselistwindow(w, type, detail):
(h, v), clicks, button = detail[:3]
d = w.begindrawing()
lh = d.lineheight()
if 0 <= v < lh*len(w.list):
i = v / lh
else:
i = -1
if w.selected <> i:
hideselection(w, d)
w.selected = i
showselection(w, d)
d.close()
if type == WE_MOUSE_DOWN and clicks >= 2 and i >= 0:
setcursors('watch')
name, info = w.list[i]
fullname = os.path.join(w.dirname, name)
if info == '/':
if clicks == 2:
G.windows.append(openlistwindow(fullname))
else:
playfile(fullname)
setcursors('cross')
def closelistwindow(w):
G.windows.remove(w)
def setcursors(cursor):
for w in G.windows:
w.setwincursor(cursor)
G.cw.win.setwincursor(cursor)
# Playing tools
cache = {}
def clearcache():
for x in cache.keys():
cmd = 'rm -f ' + cache[x]
if G.debug: print cmd
sts = os.system(cmd)
if sts:
print cmd
print 'Exit status', sts
del cache[x]
validrates = (8000, 11025, 16000, 22050, 32000, 44100, 48000)
def playfile(filename):
killchild()
try:
tuple = sndhdr.what(filename)
except IOError, msg:
print 'Can\'t open', filename, msg
stdwin.fleep()
return
raw = 0
if tuple:
mode, rate = tuple[:2]
if rate == 0:
rate = G.rate
if rate == 0:
rate = 8000
else:
mode = G.mode
rate = G.rate
if G.debug: print 'mode =', mode, 'rate =', rate
if mode in ('au', 'aiff', 'wav', 'aifc', 'ul', 'ub', 'sb') and \
rate in validrates:
tempname = filename
if mode in ('ul', 'ub', 'sb'):
raw = 1
elif cache.has_key(filename):
tempname = cache[filename]
else:
tempname = G.tempprefix + `rand.rand()` + '.aiff'
cmd = SOX
if G.debug:
cmd = cmd + ' -V'
if mode <> '':
cmd = cmd + ' -t ' + mode
cmd = cmd + ' ' + commands.mkarg(filename)
cmd = cmd + ' -t aiff'
if rate not in validrates:
rate = 32000
if rate:
cmd = cmd + ' -r ' + `rate`
cmd = cmd + ' ' + tempname
if G.debug: print cmd
sts = os.system(cmd)
if sts:
print cmd
print 'Exit status', sts
stdwin.fleep()
try:
os.unlink(tempname)
except:
pass
return
cache[filename] = tempname
if raw:
pid = sfplayraw(tempname, tuple)
else:
pid = sfplay(tempname, [])
if G.synchronous:
sts = os.wait(pid, 0)
else:
G.busy = pid
G.stop.enable(1)
G.cw.win.settimer(1)
def sfplayraw(filename, tuple):
args = ['-i']
type, rate, channels, frames, bits = tuple
if type == 'ul':
args.append('mulaw')
elif type == 'ub':
args = args + ['integer', '8', 'unsigned']
elif type == 'sb':
args = args + ['integer', '8', '2scomp']
else:
print 'sfplayraw: warning: unknown type in', tuple
if channels > 1:
args = args + ['channels', `channels`]
if not rate:
rate = G.rate
if rate:
args = args + ['rate', `rate`]
args.append('end')
return sfplay(filename, args)
def sfplay(filename, args):
if G.debug:
args = ['-p'] + args
args = [SFPLAY, '-r'] + args + [filename]
if G.debug: print 'sfplay:', args
pid = os.fork()
if pid == 0:
# Child
os.execv(SFPLAY, args)
# NOTREACHED
else:
# Parent
return pid
main()

View File

@ -1,198 +0,0 @@
#! /usr/bin/env python
# Watch line printer queues (only works with BSD 4.3 lpq).
#
# This brings up a window containing one line per printer argument.
#
# Each line gives a small summary of the printer's status and queue.
# The status tries to give as much relevant information as possible,
# and gives extra info if you have jobs in the queue.
#
# The line's background color gives a hint at the status: navajo white
# for idle, green if your job is now printing, yellow/orange for
# small/large queue, red for errors.
#
# To reduce the duration of the unresponsive time while it is waiting
# for an lpq subprocess to finish, it polls one printer every
# delay/len(printers) seconds. A tiny dot indicates the last printer
# updated. Hit the mouse button in the window to update the next one.
#
# To do:
# - add an argument to override the default delay
# - add arguments to override the default colors
# - better heuristic for small/large queue (and more colors!)
# - mouse clicks should update the printer clicked in
# - better visual appearance, e.g., boxes around the lines?
import posix
import sys
import time
import string
import stdwin
from stdwinevents import *
import mainloop
# Default parameters
DEF_PRINTER = 'oce' # This is CWI specific!
DEF_DELAY = 10
# Color assignments
c_unknown = stdwin.fetchcolor('white')
c_idle = stdwin.fetchcolor('navajo white')
c_ontop = stdwin.fetchcolor('green')
c_smallqueue = stdwin.fetchcolor('yellow')
c_bigqueue = stdwin.fetchcolor('orange')
c_error = stdwin.fetchcolor('red')
def main():
delay = DEF_DELAY
#
try:
thisuser = posix.environ['LOGNAME']
except:
thisuser = posix.environ['USER']
#
printers = sys.argv[1:]
if printers:
# Strip '-P' from printer names just in case
# the user specified it...
for i in range(len(printers)):
if printers[i][:2] == '-P':
printers[i] = printers[i][2:]
else:
if posix.environ.has_key('PRINTER'):
printers = [posix.environ['PRINTER']]
else:
printers = [DEF_PRINTER]
#
width = stdwin.textwidth('in')*20
height = len(printers) * stdwin.lineheight() + 5
stdwin.setdefwinsize(width, height)
stdwin.setdefscrollbars(0, 0)
#
win = stdwin.open('lpwin')
#
win.printers = printers
win.colors = [c_unknown] * len(printers)
win.texts = printers[:]
win.next = 0
win.delay = DEF_DELAY
win.thisuser = thisuser
win.dispatch = lpdispatch
#
win.settimer(1)
#
mainloop.register(win)
mainloop.mainloop()
def lpdispatch(event):
type, win, detail = event
if type == WE_CLOSE or type == WE_CHAR and detail in ('q', 'Q'):
mainloop.unregister(win)
elif type == WE_DRAW:
drawproc(win)
elif type == WE_TIMER:
update(win)
win.change((0,0), (10000, 10000))
elif type == WE_MOUSE_UP:
win.settimer(1)
def drawproc(win):
d = win.begindrawing()
offset = d.textwidth('.')
h, v = 0, 0
for i in range(len(win.printers)):
text = win.texts[i]
color = win.colors[i]
d.setbgcolor(color)
d.erase((h, v), (h+10000, v+d.lineheight()))
if (i+1) % len(win.printers) == win.next and color <> c_unknown:
d.text((h, v), '.')
d.text((h+offset, v), text)
v = v + d.lineheight()
def update(win):
i = win.next
win.next = (i+1) % len(win.printers)
win.texts[i], win.colors[i] = makestatus(win.printers[i], win.thisuser)
win.settimer(int(win.delay * 10.0 / len(win.printers)))
def makestatus(name, thisuser):
pipe = posix.popen('lpq -P' + name + ' 2>&1', 'r')
lines = []
users = {}
aheadbytes = 0
aheadjobs = 0
userseen = 0
totalbytes = 0
totaljobs = 0
color = c_unknown
while 1:
line = pipe.readline()
if not line: break
fields = string.split(line)
n = len(fields)
if len(fields) >= 6 and fields[n-1] == 'bytes':
rank = fields[0]
user = fields[1]
job = fields[2]
files = fields[3:-2]
bytes = eval(fields[n-2])
if user == thisuser:
userseen = 1
if aheadjobs == 0:
color = c_ontop
elif not userseen:
aheadbytes = aheadbytes + bytes
aheadjobs = aheadjobs + 1
totalbytes = totalbytes + bytes
totaljobs = totaljobs + 1
if color == c_unknown:
color = c_smallqueue
elif color == c_smallqueue:
color = c_bigqueue
if users.has_key(user):
ujobs, ubytes = users[user]
else:
ujobs, ubytes = 0, 0
ujobs = ujobs + 1
ubytes = ubytes + bytes
users[user] = ujobs, ubytes
else:
if fields and fields[0] <> 'Rank':
line = string.strip(line)
if line == 'no entries':
line = name + ': idle'
if color == c_unknown:
color = c_idle
elif line[-22:] == ' is ready and printing':
line = line[:-22]
else:
line = name + ': ' + line
color = c_error
lines.append(line)
#
if totaljobs:
line = `(totalbytes+1023)/1024` + ' K'
if totaljobs <> len(users):
line = line + ' (' + `totaljobs` + ' jobs)'
if len(users) == 1:
line = line + ' for ' + users.keys()[0]
else:
line = line + ' for ' + `len(users)` + ' users'
if userseen:
if aheadjobs == 0:
line = line + ' (' + thisuser + ' first)'
else:
line = line + ' (' + `(aheadbytes+1023)/1024`
line = line + ' K before ' + thisuser + ')'
lines.append(line)
#
sts = pipe.close()
if sts:
lines.append('lpq exit status ' + `sts`)
color = c_error
return string.joinfields(lines, ': '), color
main()

View File

@ -1,183 +0,0 @@
#! /usr/bin/env python
# A minimal single-window text editor using STDWIN's text objects.
#
# Usage: microedit file
#
# This is not intended as a real application but as an introduction
# to STDWIN programming in Python, especially text objects.
# Once you understand microedit.py, study miniedit.py to learn
# about multiple windows and menus, cut and paste, etc.
import sys
import stdwin
from stdwinevents import *
# Main program
#
def main():
#
# Get the filename argument and read its contents as one very
# large string.
# An exception will terminate the program if there is no argument
# or if the file could not be read...
#
filename = sys.argv[1]
fp = open(filename, 'r')
contents = fp.read()
del fp # Close the file
#
# Create the window, using the filename as window title
#
window = stdwin.open(filename)
#
# Add a simple File menu to the window with two items
#
filemenu = window.menucreate('File')
filemenu.additem('Save', 'S') # Item 0 (shortcut Meta-S)
filemenu.additem('Save As...') # Item 1
#
# Create a text object occupying the entire window
# and fill it with the file's contents
#
corner = window.getwinsize() # (width, height)
area = (0, 0), corner # Rectangle as large as the window
text = window.textcreate(area)
text.settext(contents)
del contents # Get rid of contents object
fix_textsize(window, text) # Set document size accordingly
#
# Main event loop -- stop if a close request comes in.
#
# STDWIN applications should regularly call stdwin.getevent()
# otherwise the windows won't function as expected.
#
while 1:
#
# Get the next event
#
type, w, detail = e = stdwin.getevent()
#
# Event decoding switch
#
if type == WE_CLOSE:
break # Stop (no check for saved file!)
elif type == WE_SIZE:
#
# The window was resized --
# let the text object recompute the line breaks
# and change the document size accordingly,
# so scroll bars will work
#
fix_textsize(window, text)
elif type == WE_MENU:
#
# Execute a file menu request (our only menu)
#
menu, item = detail
if item == 0:
#
# "Save": save to the current filename
#
dummy = save_file(window, text, filename)
elif item == 1:
#
# "Save As": ask a new filename, save to it,
# and make it the current filename
#
# NB: askfile raises KeyboardInterrupt
# if the user cancels the dialog, hence
# the try statement
#
try:
newfile = stdwin.askfile( \
'Save as:', filename, 1)
except KeyboardInterrupt:
newfile = ''
if newfile:
if save_file(window, text, newfile):
filename = newfile
window.settitle(filename)
elif text.event(e):
#
# The text object has handled the event.
# Fix the document size if necessary.
# Note: this sometimes fixes the size
# unnecessarily, e.g., for arrow keys.
#
if type in (WE_CHAR, WE_COMMAND):
fix_docsize(window, text)
# Save the window's contents to the filename.
# If the open() fails, put up a warning message and return 0;
# if the save succeeds, return 1.
#
def save_file(window, text, filename):
#
# Open the file for writing, handling exceptions
#
try:
fp = open(filename, 'w')
except RuntimeError:
stdwin.message('Cannot create ' + filename)
return 0
#
# Get the contents of the text object as one very long string
#
contents = text.gettext()
#
# Write the contents to the file
#
fp.write(contents)
#
# The file is automatically closed when this routine returns
#
return 1
# Change the size of the text object to fit in the window,
# and then fix the window's document size to fit around the text object.
#
def fix_textsize(window, text):
#
# Compute a rectangle as large as the window
#
corner = window.getwinsize() # (width, height)
area = (0, 0), (corner)
#
# Move the text object to this rectangle.
# Note: text.move() ignores the bottom coordinate!
#
text.move(area)
#
# Now fix the document size accordingly
#
fix_docsize(window, text)
# Fix the document size, after the text has changed
#
def fix_docsize(window, text):
#
# Get the actual rectangle occupied by the text object.
# This has the same left, top and right, but a different bottom.
#
area = text.getrect()
#
# Compute the true height of the text object
#
origin, corner = area
width, height = corner
#
# Set the document height to the text object's height.
# The width is zero since we don't want a horizontal scroll bar.
#
window.setdocsize(0, height)
# Once all functions are defined, call main()
#
main()

View File

@ -1,356 +0,0 @@
#! /usr/bin/env python
# A miniature multi-window editor using STDWIN's text objects.
#
# Usage: miniedit [file] ...
#
# The user interface is similar to that of the miniedit demo application
# in C that comes with STDWIN.
#
# XXX need to comment the functions
# XXX Not yet implemented:
# disabling menu entries for inapplicable actions
# Find operations
import sys
import stdwin
from stdwinevents import *
# Constant: list of WE_COMMAND events that (may) change the text buffer
# so we can decide whether to set the 'changed' flag.
# Note that it is possible for such a command to fail (a backspace
# at the beginning of the buffer) but we'll set the changed flag anyway
# -- it's too complicated to check this condition right now.
#
changing = [WC_RETURN, WC_TAB, WC_BACKSPACE]
# The list of currently open windows;
# this is maintained so we can stop when there are no windows left
#
windows = []
# A note on window data attributes (set by open_window):
#
# w.textobject the window's text object
# w.changed true when the window's text is changed
# w.filename filename connected to the window; '' if none
# Main program
#
def main():
#
# Set a reasonable default window size.
# If we are using a fixed-width font this will open a 80x24 window;
# for variable-width fonts we approximate this based on an average
#
stdwin.setdefwinsize(40*stdwin.textwidth('in'), 24*stdwin.lineheight())
#
# Create global menus (as local variables)
#
filemenu = make_file_menu(stdwin)
editmenu = make_edit_menu(stdwin)
findmenu = make_find_menu(stdwin)
#
# Get the list of files from the command line (maybe none)
#
files = sys.argv[1:]
#
# Open any files -- errors will be reported but do won't stop us
#
for filename in files:
open_file(filename)
#
# If there were no files, or none of them could be opened,
# put up a dialog asking for a filename
#
if not windows:
try:
open_dialog(None)
except KeyboardInterrupt:
pass # User cancelled
#
# If the dialog was cancelled, create an empty new window
#
if not windows:
new_window(None)
#
# Main event loop -- stop when we have no open windows left
#
while windows:
#
# Get the next event -- ignore interrupts
#
try:
type, window, detail = event = stdwin.getevent()
except KeyboardInterrupt:
type, window, detail = event = WE_NONE, None, None
#
# Event decoding switch
#
if not window:
pass # Ignore such events
elif type == WE_MENU:
#
# Execute menu operation
#
menu, item = detail
try:
menu.actions[item](window)
except KeyboardInterrupt:
pass # User cancelled
elif type == WE_CLOSE:
#
# Close a window
#
try:
close_dialog(window)
except KeyboardInterrupt:
pass # User cancelled
elif type == WE_SIZE:
#
# A window was resized --
# let the text object recompute the line breaks
# and change the document size accordingly,
# so scroll bars will work
#
fix_textsize(window)
elif window.textobject.event(event):
#
# The event was eaten by the text object --
# set the changed flag if not already set
#
if type == WE_CHAR or \
type == WE_COMMAND and detail in changing:
window.changed = 1
fix_docsize(window)
#
# Delete all objects that may still reference the window
# in the event -- this is needed otherwise the window
# won't actually be closed and may receive further
# events, which will confuse the event decoder
#
del type, window, detail, event
def make_file_menu(object):
menu = object.menucreate('File')
menu.actions = []
additem(menu, 'New', 'N', new_window)
additem(menu, 'Open..', 'O', open_dialog)
additem(menu, '', '', None)
additem(menu, 'Save', 'S', save_dialog)
additem(menu, 'Save As..', '', save_as_dialog)
additem(menu, 'Save a Copy..', '', save_copy_dialog)
additem(menu, 'Revert', 'R', revert_dialog)
additem(menu, 'Quit', 'Q', quit_dialog)
return menu
def make_edit_menu(object):
menu = object.menucreate('Edit')
menu.actions = []
additem(menu, 'Cut', 'X', do_cut)
additem(menu, 'Copy', 'C', do_copy)
additem(menu, 'Paste', 'V', do_paste)
additem(menu, 'Clear', 'B', do_clear)
additem(menu, 'Select All', 'A', do_select_all)
return menu
def make_find_menu(object):
menu = object.menucreate('Find')
menu.actions = []
# XXX
return menu
def additem(menu, text, shortcut, function):
if shortcut:
menu.additem(text, shortcut)
else:
menu.additem(text)
menu.actions.append(function)
def open_dialog(current_ignored):
filename = stdwin.askfile('Open file:', '', 0)
open_file(filename)
def open_file(filename):
try:
fp = open(filename, 'r')
except RuntimeError:
stdwin.message(filename + ': cannot open')
return # Error, forget it
try:
contents = fp.read()
except RuntimeError:
stdwin.message(filename + ': read error')
return # Error, forget it
del fp # Close the file
open_window(filename, filename, contents)
def new_window(current_ignored):
open_window('', 'Untitled', '')
def open_window(filename, title, contents):
try:
window = stdwin.open(title)
except RuntimeError:
stdwin.message('cannot open new window')
return # Error, forget it
window.textobject = window.textcreate((0, 0), window.getwinsize())
window.textobject.settext(contents)
window.changed = 0
window.filename = filename
fix_textsize(window)
windows.append(window)
def quit_dialog(window):
for window in windows[:]:
close_dialog(window)
def close_dialog(window):
if window.changed:
prompt = 'Save changes to ' + window.gettitle() + ' ?'
if stdwin.askync(prompt, 1):
save_dialog(window)
if window.changed:
return # Save failed (not) cancelled
windows.remove(window)
del window.textobject
def save_dialog(window):
if not window.filename:
save_as_dialog(window)
return
if save_file(window, window.filename):
window.changed = 0
def save_as_dialog(window):
prompt = 'Save ' + window.gettitle() + ' as:'
filename = stdwin.askfile(prompt, window.filename, 1)
if save_file(window, filename):
window.filename = filename
window.settitle(filename)
window.changed = 0
def save_copy_dialog(window):
prompt = 'Save a copy of ' + window.gettitle() + ' as:'
filename = stdwin.askfile(prompt, window.filename, 1)
void = save_file(window, filename)
def save_file(window, filename):
try:
fp = open(filename, 'w')
except RuntimeError:
stdwin.message(filename + ': cannot create')
return 0
contents = window.textobject.gettext()
try:
fp.write(contents)
except RuntimeError:
stdwin.message(filename + ': write error')
return 0
return 1
def revert_dialog(window):
if not window.filename:
stdwin.message('This window has no file to revert from')
return
if window.changed:
prompt = 'Really read ' + window.filename + ' back from file?'
if not stdwin.askync(prompt, 1):
return
try:
fp = open(window.filename, 'r')
except RuntimeError:
stdwin.message(filename + ': cannot open')
return
contents = fp.read()
del fp # Close the file
window.textobject.settext(contents)
window.changed = 0
fix_docsize(window)
def fix_textsize(window):
corner = window.getwinsize()
area = (0, 0), (corner)
window.textobject.move(area)
fix_docsize(window)
def fix_docsize(window):
area = window.textobject.getrect()
origin, corner = area
width, height = corner
window.setdocsize(0, height)
def do_cut(window):
selection = window.textobject.getfocustext()
if not selection:
stdwin.fleep() # Nothing to cut
elif not window.setselection(WS_PRIMARY, selection):
stdwin.fleep() # Window manager glitch...
else:
stdwin.rotatecutbuffers(1)
stdwin.setcutbuffer(0, selection)
window.textobject.replace('')
window.changed = 1
fix_docsize(window)
def do_copy(window):
selection = window.textobject.getfocustext()
if not selection:
stdwin.fleep() # Nothing to cut
elif not window.setselection(WS_PRIMARY, selection):
stdwin.fleep() # Window manager glitch...
else:
stdwin.rotatecutbuffers(1)
stdwin.setcutbuffer(0, selection)
def do_paste(window):
selection = stdwin.getselection(WS_PRIMARY)
if not selection:
selection = stdwin.getcutbuffer(0)
if not selection:
stdwin.fleep() # Nothing to paste
else:
window.textobject.replace(selection)
window.changed = 1
fix_docsize(window)
def do_clear(window):
first, last = window.textobject.getfocus()
if first == last:
stdwin.fleep() # Nothing to clear
else:
window.textobject.replace('')
window.changed = 1
fix_docsize(window)
def do_select_all(window):
window.textobject.setfocus(0, 0x7fffffff) # XXX Smaller on the Mac!
main()

View File

@ -1,449 +0,0 @@
#! /usr/bin/env python
# A STDWIN-based front end for the Python interpreter.
#
# This is useful if you want to avoid console I/O and instead
# use text windows to issue commands to the interpreter.
#
# It supports multiple interpreter windows, each with its own context.
#
# BUGS AND CAVEATS:
#
# This was written long ago as a demonstration, and slightly hacked to
# keep it up-to-date, but never as an industry-strength alternative
# interface to Python. It should be rewritten using more classes, and
# merged with something like wdb.
#
# Although this supports multiple windows, the whole application
# is deaf and dumb when a command is running in one window.
#
# Interrupt is (ab)used to signal EOF on input requests.
#
# On UNIX (using X11), interrupts typed in the window will not be
# seen until the next input or output operation. When you are stuck
# in an infinite loop, try typing ^C in the shell window where you
# started this interpreter. (On the Mac, interrupts work normally.)
import sys
import stdwin
from stdwinevents import *
import rand
import mainloop
import os
# Stack of windows waiting for [raw_]input().
# Element [0] is the top.
# If there are multiple windows waiting for input, only the
# one on top of the stack can accept input, because the way
# raw_input() is implemented (using recursive mainloop() calls).
#
inputwindows = []
# Exception raised when input is available
#
InputAvailable = 'input available for raw_input (not an error)'
# Main program -- create the window and call the mainloop
#
def main():
# Hack so 'import python' won't load another copy
# of this if we were loaded though 'python python.py'.
# (Should really look at sys.argv[0]...)
if 'inputwindows' in dir(sys.modules['__main__']) and \
sys.modules['__main__'].inputwindows is inputwindows:
sys.modules['python'] = sys.modules['__main__']
#
win = makewindow()
mainloop.mainloop()
# Create a new window
#
def makewindow():
# stdwin.setdefscrollbars(0, 1) # Not in Python 0.9.1
# stdwin.setfont('monaco') # Not on UNIX! and not Python 0.9.1
# width, height = stdwin.textwidth('in')*40, stdwin.lineheight()*24
# stdwin.setdefwinsize(width, height)
win = stdwin.open('Python interpreter ready')
win.editor = win.textcreate((0,0), win.getwinsize())
win.globals = {} # Dictionary for user's globals
win.command = '' # Partially read command
win.busy = 0 # Ready to accept a command
win.auto = 1 # [CR] executes command
win.insertOutput = 1 # Insert output at focus
win.insertError = 1 # Insert error output at focus
win.setwincursor('ibeam')
win.filename = '' # Empty if no file for this window
makefilemenu(win)
makeeditmenu(win)
win.dispatch = pdispatch # Event dispatch function
mainloop.register(win)
return win
# Make a 'File' menu
#
def makefilemenu(win):
win.filemenu = mp = win.menucreate('File')
mp.callback = []
additem(mp, 'New', 'N', do_new)
additem(mp, 'Open...', 'O', do_open)
additem(mp, '', '', None)
additem(mp, 'Close', 'W', do_close)
additem(mp, 'Save', 'S', do_save)
additem(mp, 'Save as...', '', do_saveas)
additem(mp, '', '', None)
additem(mp, 'Quit', 'Q', do_quit)
# Make an 'Edit' menu
#
def makeeditmenu(win):
win.editmenu = mp = win.menucreate('Edit')
mp.callback = []
additem(mp, 'Cut', 'X', do_cut)
additem(mp, 'Copy', 'C', do_copy)
additem(mp, 'Paste', 'V', do_paste)
additem(mp, 'Clear', '', do_clear)
additem(mp, '', '', None)
win.iauto = len(mp.callback)
additem(mp, 'Autoexecute', '', do_auto)
mp.check(win.iauto, win.auto)
win.insertOutputNum = len(mp.callback)
additem(mp, 'Insert Output', '', do_insertOutputOption)
win.insertErrorNum = len(mp.callback)
additem(mp, 'Insert Error', '', do_insertErrorOption)
additem(mp, 'Exec', '\r', do_exec)
# Helper to add a menu item and callback function
#
def additem(mp, text, shortcut, handler):
if shortcut:
mp.additem(text, shortcut)
else:
mp.additem(text)
mp.callback.append(handler)
# Dispatch a single event to the interpreter.
# Resize events cause a resize of the editor.
# Some events are treated specially.
# Most other events are passed directly to the editor.
#
def pdispatch(event):
type, win, detail = event
if not win:
win = stdwin.getactive()
if not win: return
if type == WE_CLOSE:
do_close(win)
return
elif type == WE_SIZE:
win.editor.move((0, 0), win.getwinsize())
elif type == WE_COMMAND and detail == WC_RETURN:
if win.auto:
do_exec(win)
else:
void = win.editor.event(event)
elif type == WE_COMMAND and detail == WC_CANCEL:
if win.busy:
raise KeyboardInterrupt
else:
win.command = ''
settitle(win)
elif type == WE_MENU:
mp, item = detail
mp.callback[item](win)
else:
void = win.editor.event(event)
if win in mainloop.windows:
# May have been deleted by close...
win.setdocsize(0, win.editor.getrect()[1][1])
if type in (WE_CHAR, WE_COMMAND):
win.editor.setfocus(win.editor.getfocus())
# Helper to set the title of the window
#
def settitle(win):
if win.filename == '':
win.settitle('Python interpreter ready')
else:
win.settitle(win.filename)
# Helper to replace the text of the focus
#
def replace(win, text):
win.editor.replace(text)
# Resize the window to display the text
win.setdocsize(0, win.editor.getrect()[1][1]) # update the size before
win.editor.setfocus(win.editor.getfocus()) # move focus to the change
# File menu handlers
#
def do_new(win):
win = makewindow()
#
def do_open(win):
try:
filename = stdwin.askfile('Open file', '', 0)
win = makewindow()
win.filename = filename
win.editor.replace(open(filename, 'r').read())
win.editor.setfocus(0, 0)
win.settitle(win.filename)
#
except KeyboardInterrupt:
pass # Don't give an error on cancel
#
def do_save(win):
try:
if win.filename == '':
win.filename = stdwin.askfile('Open file', '', 1)
f = open(win.filename, 'w')
f.write(win.editor.gettext())
#
except KeyboardInterrupt:
pass # Don't give an error on cancel
def do_saveas(win):
currentFilename = win.filename
win.filename = ''
do_save(win) # Use do_save with empty filename
if win.filename == '': # Restore the name if do_save did not set it
win.filename = currentFilename
#
def do_close(win):
if win.busy:
stdwin.message('Can\'t close busy window')
return # need to fail if quitting??
win.editor = None # Break circular reference
#del win.editmenu # What about the filemenu??
mainloop.unregister(win)
win.close()
#
def do_quit(win):
# Call win.dispatch instead of do_close because there
# may be 'alien' windows in the list.
for win in mainloop.windows[:]:
mainloop.dispatch((WE_CLOSE, win, None))
# need to catch failed close
# Edit menu handlers
#
def do_cut(win):
text = win.editor.getfocustext()
if not text:
stdwin.fleep()
return
stdwin.setcutbuffer(0, text)
replace(win, '')
#
def do_copy(win):
text = win.editor.getfocustext()
if not text:
stdwin.fleep()
return
stdwin.setcutbuffer(0, text)
#
def do_paste(win):
text = stdwin.getcutbuffer(0)
if not text:
stdwin.fleep()
return
replace(win, text)
#
def do_clear(win):
replace(win, '')
# These would be better in a preferences dialog:
#
def do_auto(win):
win.auto = (not win.auto)
win.editmenu.check(win.iauto, win.auto)
#
def do_insertOutputOption(win):
win.insertOutput = (not win.insertOutput)
title = ['Append Output', 'Insert Output'][win.insertOutput]
win.editmenu.setitem(win.insertOutputNum, title)
#
def do_insertErrorOption(win):
win.insertError = (not win.insertError)
title = ['Error Dialog', 'Insert Error'][win.insertError]
win.editmenu.setitem(win.insertErrorNum, title)
# Extract a command from the editor and execute it, or pass input to
# an interpreter waiting for it.
# Incomplete commands are merely placed in the window's command buffer.
# All exceptions occurring during the execution are caught and reported.
# (Tracebacks are currently not possible, as the interpreter does not
# save the traceback pointer until it reaches its outermost level.)
#
def do_exec(win):
if win.busy:
if win not in inputwindows:
stdwin.message('Can\'t run recursive commands')
return
if win <> inputwindows[0]:
stdwin.message('Please complete recursive input first')
return
#
# Set text to the string to execute.
a, b = win.editor.getfocus()
alltext = win.editor.gettext()
n = len(alltext)
if a == b:
# There is no selected text, just an insert point;
# so execute the current line.
while 0 < a and alltext[a-1] <> '\n': # Find beginning of line
a = a-1
while b < n and alltext[b] <> '\n': # Find end of line after b
b = b+1
text = alltext[a:b] + '\n'
else:
# Execute exactly the selected text.
text = win.editor.getfocustext()
if text[-1:] <> '\n': # Make sure text ends with \n
text = text + '\n'
while b < n and alltext[b] <> '\n': # Find end of line after b
b = b+1
#
# Set the focus to expect the output, since there is always something.
# Output will be inserted at end of line after current focus,
# or appended to the end of the text.
b = [n, b][win.insertOutput]
win.editor.setfocus(b, b)
#
# Make sure there is a preceeding newline.
if alltext[b-1:b] <> '\n':
win.editor.replace('\n')
#
#
if win.busy:
# Send it to raw_input() below
raise InputAvailable, text
#
# Like the real Python interpreter, we want to execute
# single-line commands immediately, but save multi-line
# commands until they are terminated by a blank line.
# Unlike the real Python interpreter, we don't do any syntax
# checking while saving up parts of a multi-line command.
#
# The current heuristic to determine whether a command is
# the first line of a multi-line command simply checks whether
# the command ends in a colon (followed by a newline).
# This is not very robust (comments and continuations will
# confuse it), but it is usable, and simple to implement.
# (It even has the advantage that single-line loops etc.
# don't need te be terminated by a blank line.)
#
if win.command:
# Already continuing
win.command = win.command + text
if win.command[-2:] <> '\n\n':
win.settitle('Unfinished command...')
return # Need more...
else:
# New command
win.command = text
if text[-2:] == ':\n':
win.settitle('Unfinished command...')
return
command = win.command
win.command = ''
win.settitle('Executing command...')
#
# Some hacks:
# - The standard files are replaced by an IOWindow instance.
# - A 2nd argument to exec() is used to specify the directory
# holding the user's global variables. (If this wasn't done,
# the exec would be executed in the current local environment,
# and the user's assignments to globals would be lost...)
#
save_stdin = sys.stdin
save_stdout = sys.stdout
save_stderr = sys.stderr
try:
sys.stdin = sys.stdout = sys.stderr = IOWindow(win)
win.busy = 1
try:
exec(command, win.globals)
except KeyboardInterrupt:
print '[Interrupt]'
except:
if type(sys.exc_type) == type(''):
msg = sys.exc_type
else: msg = sys.exc_type.__name__
if sys.exc_value <> None:
msg = msg + ': ' + `sys.exc_value`
if win.insertError:
stdwin.fleep()
replace(win, msg + '\n')
else:
win.settitle('Unhandled exception')
stdwin.message(msg)
finally:
# Restore redirected I/O in *all* cases
win.busy = 0
sys.stderr = save_stderr
sys.stdout = save_stdout
sys.stdin = save_stdin
settitle(win)
# Class emulating file I/O from/to a window
#
class IOWindow:
#
def __init__(self, win):
self.win = win
#
def readline(self, *unused_args):
n = len(inputwindows)
save_title = self.win.gettitle()
title = n*'(' + 'Requesting input...' + ')'*n
self.win.settitle(title)
inputwindows.insert(0, self.win)
try:
try:
mainloop.mainloop()
finally:
del inputwindows[0]
self.win.settitle(save_title)
except InputAvailable, val: # See do_exec above
return val
except KeyboardInterrupt:
raise EOFError # Until we have a "send EOF" key
# If we didn't catch InputAvailable, something's wrong...
raise EOFError
#
def write(self, text):
mainloop.check()
replace(self.win, text)
mainloop.check()
# Currently unused function to test a command's syntax without executing it
#
def testsyntax(s):
import string
lines = string.splitfields(s, '\n')
for i in range(len(lines)): lines[i] = '\t' + lines[i]
lines.insert(0, 'if 0:')
lines.append('')
exec(string.joinfields(lines, '\n'))
# Call the main program
#
main()

View File

@ -1,484 +0,0 @@
#! /usr/bin/env python
# A window-oriented recursive diff utility.
# NB: This uses undocumented window classing modules.
# TO DO:
# - faster update after moving/copying one file
# - diff flags (-b, etc.) should be global or maintained per window
# - use a few fixed windows instead of creating new ones all the time
# - ways to specify patterns to skip
# (best by pointing at a file and clicking a special menu entry!)
# - add rcsdiff menu commands
# - add a way to view status of selected files without opening them
# - add a way to diff two files with different names
# - add a way to rename files
# - keep backups of overwritten/deleted files
# - a way to mark specified files as uninteresting for dircmp
import sys
import os
import rand
import commands
import dircache
import statcache
import cmp
import cmpcache
import stdwin
import gwin
import textwin
import filewin
import tablewin
import anywin
mkarg = commands.mkarg
mk2arg = commands.mk2arg
# List of names to ignore in dircmp()
#
skiplist = ['RCS', 'CVS', '.Amake', 'tags', 'TAGS', '.', '..']
# Function to determine whether a name should be ignored in dircmp().
#
def skipthis(file):
return file[-1:] == '~' or file in skiplist
def anydiff(a, b, flags): # Display differences between any two objects
print 'diff', flags, a, b
if os.path.isdir(a) and os.path.isdir(b):
w = dirdiff(a, b, flags)
else:
w = filediff(a, b, flags)
addstatmenu(w, [a, b])
w.original_close = w.close
w.close = close_dirwin
return w
def close_dirwin(w):
close_subwindows(w, (), 0)
w.original_close(w)
def filediff(a, b, flags): # Display differences between two text files
diffcmd = 'diff'
if flags: diffcmd = diffcmd + mkarg(flags)
diffcmd = diffcmd + mkarg(a) + mkarg(b)
difftext = commands.getoutput(diffcmd)
return textwin.open_readonly(mktitle(a, b), difftext)
def dirdiff(a, b, flags): # Display differences between two directories
data = diffdata(a, b, flags)
w = tablewin.open(mktitle(a, b), data)
w.flags = flags
w.a = a
w.b = b
addviewmenu(w)
addactionmenu(w)
return w
def diffdata(a, b, flags): # Compute directory differences.
#
a_only = [('A only:', header_action), ('', header_action)]
b_only = [('B only:', header_action), ('', header_action)]
ab_diff = [('A <> B:', header_action), ('', header_action)]
ab_same = [('A == B:', header_action), ('', header_action)]
data = [a_only, b_only, ab_diff, ab_same]
#
a_list = dircache.listdir(a)[:]
b_list = dircache.listdir(b)[:]
dircache.annotate(a, a_list)
dircache.annotate(b, b_list)
a_list.sort()
b_list.sort()
#
for x in a_list:
if x in ['./', '../']:
pass
elif x not in b_list:
a_only.append((x, a_only_action))
else:
ax = os.path.join(a, x)
bx = os.path.join(b, x)
if os.path.isdir(ax) and os.path.isdir(bx):
if flags == '-r':
same = dircmp(ax, bx)
else:
same = 0
else:
try:
same = cmp.cmp(ax, bx)
except (RuntimeError, os.error):
same = 0
if same:
ab_same.append((x, ab_same_action))
else:
ab_diff.append((x, ab_diff_action))
#
for x in b_list:
if x in ['./', '../']:
pass
elif x not in a_list:
b_only.append((x, b_only_action))
#
return data
# Re-read the directory.
# Attempt to find the selected item back.
def update(w):
setbusy(w)
icol, irow = w.selection
if 0 <= icol < len(w.data) and 2 <= irow < len(w.data[icol]):
selname = w.data[icol][irow][0]
else:
selname = ''
statcache.forget_dir(w.a)
statcache.forget_dir(w.b)
tablewin.select(w, (-1, -1))
tablewin.update(w, diffdata(w.a, w.b, w.flags))
if selname:
for icol in range(len(w.data)):
for irow in range(2, len(w.data[icol])):
if w.data[icol][irow][0] == selname:
tablewin.select(w, (icol, irow))
break
# Action functions for table items in directory diff windows
def header_action(w, string, (icol, irow), (pos, clicks, button, mask)):
tablewin.select(w, (-1, -1))
def a_only_action(w, string, (icol, irow), (pos, clicks, button, mask)):
tablewin.select(w, (icol, irow))
if clicks == 2:
w2 = anyopen(os.path.join(w.a, string))
if w2:
w2.parent = w
def b_only_action(w, string, (icol, irow), (pos, clicks, button, mask)):
tablewin.select(w, (icol, irow))
if clicks == 2:
w2 = anyopen(os.path.join(w.b, string))
if w2:
w2.parent = w
def ab_diff_action(w, string, (icol, irow), (pos, clicks, button, mask)):
tablewin.select(w, (icol, irow))
if clicks == 2:
w2 = anydiff(os.path.join(w.a, string), os.path.join(w.b, string),'')
w2.parent = w
def ab_same_action(w, string, sel, detail):
ax = os.path.join(w.a, string)
if os.path.isdir(ax):
ab_diff_action(w, string, sel, detail)
else:
a_only_action(w, string, sel, detail)
def anyopen(name): # Open any kind of document, ignore errors
try:
w = anywin.open(name)
except (RuntimeError, os.error):
stdwin.message('Can\'t open ' + name)
return 0
addstatmenu(w, [name])
return w
def dircmp(a, b): # Compare whether two directories are the same
# To make this as fast as possible, it uses the statcache
print ' dircmp', a, b
a_list = dircache.listdir(a)
b_list = dircache.listdir(b)
for x in a_list:
if skipthis(x):
pass
elif x not in b_list:
return 0
else:
ax = os.path.join(a, x)
bx = os.path.join(b, x)
if statcache.isdir(ax) and statcache.isdir(bx):
if not dircmp(ax, bx): return 0
else:
try:
if not cmpcache.cmp(ax, bx): return 0
except (RuntimeError, os.error):
return 0
for x in b_list:
if skipthis(x):
pass
elif x not in a_list:
return 0
return 1
# View menu (for dir diff windows only)
def addviewmenu(w):
w.viewmenu = m = w.menucreate('View')
m.action = []
add(m, 'diff -r A B', diffr_ab)
add(m, 'diff A B', diff_ab)
add(m, 'diff -b A B', diffb_ab)
add(m, 'diff -c A B', diffc_ab)
add(m, 'gdiff A B', gdiff_ab)
add(m, ('Open A ', 'A'), open_a)
add(m, ('Open B ', 'B'), open_b)
add(m, 'Rescan', rescan)
add(m, 'Rescan -r', rescan_r)
# Action menu (for dir diff windows only)
def addactionmenu(w):
w.actionmenu = m = w.menucreate('Action')
m.action = []
add(m, 'cp A B', cp_ab)
add(m, 'rm B', rm_b)
add(m, '', nop)
add(m, 'cp B A', cp_ba)
add(m, 'rm A', rm_a)
# Main menu (global):
def mainmenu():
m = stdwin.menucreate('Wdiff')
m.action = []
add(m, ('Quit wdiff', 'Q'), quit_wdiff)
add(m, 'Close subwindows', close_subwindows)
return m
def add(m, text, action):
m.additem(text)
m.action.append(action)
def quit_wdiff(w, m, item):
if askyesno('Really quit wdiff altogether?', 1):
sys.exit(0)
def close_subwindows(w, m, item):
while 1:
for w2 in gwin.windows:
if w2.parent == w:
close_subwindows(w2, m, item)
w2.close(w2)
break # inner loop, continue outer loop
else:
break # outer loop
def diffr_ab(w, m, item):
dodiff(w, '-r')
def diff_ab(w, m, item):
dodiff(w, '')
def diffb_ab(w, m, item):
dodiff(w, '-b')
def diffc_ab(w, m, item):
dodiff(w, '-c')
def gdiff_ab(w, m, item): # Call SGI's gdiff utility
x = getselection(w)
if x:
a, b = os.path.join(w.a, x), os.path.join(w.b, x)
if os.path.isdir(a) or os.path.isdir(b):
stdwin.fleep() # This is for files only
else:
diffcmd = 'gdiff'
diffcmd = diffcmd + mkarg(a) + mkarg(b) + ' &'
print diffcmd
sts = os.system(diffcmd)
if sts: print 'Exit status', sts
def dodiff(w, flags):
x = getselection(w)
if x:
w2 = anydiff(os.path.join(w.a, x), os.path.join(w.b, x), flags)
w2.parent = w
def open_a(w, m, item):
x = getselection(w)
if x:
w2 = anyopen(os.path.join(w.a, x))
if w2:
w2.parent = w
def open_b(w, m, item):
x = getselection(w)
if x:
w2 = anyopen(os.path.join(w.b, x))
if w2:
w2.parent = w
def rescan(w, m, item):
w.flags = ''
update(w)
def rescan_r(w, m, item):
w.flags = '-r'
update(w)
def rm_a(w, m, item):
x = getselection(w)
if x:
if x[-1:] == '/': x = x[:-1]
x = os.path.join(w.a, x)
if os.path.isdir(x):
if askyesno('Recursively remove A directory ' + x, 1):
runcmd('rm -rf' + mkarg(x))
else:
runcmd('rm -f' + mkarg(x))
update(w)
def rm_b(w, m, item):
x = getselection(w)
if x:
if x[-1:] == '/': x = x[:-1]
x = os.path.join(w.b, x)
if os.path.isdir(x):
if askyesno('Recursively remove B directory ' + x, 1):
runcmd('rm -rf' + mkarg(x))
else:
runcmd('rm -f' + mkarg(x))
update(w)
def cp_ab(w, m, item):
x = getselection(w)
if x:
if x[-1:] == '/': x = x[:-1]
ax = os.path.join(w.a, x)
bx = os.path.join(w.b, x)
if os.path.isdir(ax):
if os.path.exists(bx):
m = 'Can\'t copy directory to existing target'
stdwin.message(m)
return
runcmd('cp -r' + mkarg(ax) + mkarg(w.b))
else:
runcmd('cp' + mkarg(ax) + mk2arg(w.b, x))
update(w)
def cp_ba(w, m, item):
x = getselection(w)
if x:
if x[-1:] == '/': x = x[:-1]
ax = os.path.join(w.a, x)
bx = os.path.join(w.b, x)
if os.path.isdir(bx):
if os.path.exists(ax):
m = 'Can\'t copy directory to existing target'
stdwin.message(m)
return
runcmd('cp -r' + mkarg(bx) + mkarg(w.a))
else:
runcmd('cp' + mk2arg(w.b, x) + mkarg(ax))
update(w)
def nop(args):
pass
def getselection(w):
icol, irow = w.selection
if 0 <= icol < len(w.data):
if 0 <= irow < len(w.data[icol]):
return w.data[icol][irow][0]
stdwin.message('no selection')
return ''
def runcmd(cmd):
print cmd
sts, output = commands.getstatusoutput(cmd)
if sts or output:
if not output:
output = 'Exit status ' + `sts`
stdwin.message(output)
# Status menu (for all kinds of windows)
def addstatmenu(w, files):
w.statmenu = m = w.menucreate('Stat')
m.files = files
m.action = []
for file in files:
m.additem(commands.getstatus(file))
m.action.append(stataction)
def stataction(w, m, item): # Menu item action for stat menu
file = m.files[item]
try:
m.setitem(item, commands.getstatus(file))
except os.error:
stdwin.message('Can\'t get status for ' + file)
# Compute a suitable window title from two paths
def mktitle(a, b):
if a == b: return a
i = 1
while a[-i:] == b[-i:]: i = i+1
i = i-1
if not i:
return a + ' ' + b
else:
return '{' + a[:-i] + ',' + b[:-i] + '}' + a[-i:]
# Ask a confirmation question
def askyesno(prompt, default):
try:
return stdwin.askync(prompt, default)
except KeyboardInterrupt:
return 0
# Display a message "busy" in a window, and mark it for updating
def setbusy(w):
left, top = w.getorigin()
width, height = w.getwinsize()
right, bottom = left + width, top + height
d = w.begindrawing()
d.erase((0, 0), (10000, 10000))
text = 'Busy...'
textwidth = d.textwidth(text)
textheight = d.lineheight()
h, v = left + (width-textwidth)/2, top + (height-textheight)/2
d.text((h, v), text)
del d
w.change((0, 0), (10000, 10000))
# Main function
def main():
print 'wdiff: warning: this program does NOT make backups'
argv = sys.argv
flags = ''
if len(argv) >= 2 and argv[1][:1] == '-':
flags = argv[1]
del argv[1]
stdwin.setdefscrollbars(0, 1)
m = mainmenu() # Create menu earlier than windows
if len(argv) == 2: # 1 argument
w = anyopen(argv[1])
if not w: return
elif len(argv) == 3: # 2 arguments
w = anydiff(argv[1], argv[2], flags)
w.parent = ()
else:
sys.stdout = sys.stderr
print 'usage:', argv[0], '[diff-flags] dir-1 [dir-2]'
sys.exit(2)
del w # It's preserved in gwin.windows
while 1:
try:
gwin.mainloop()
break
except KeyboardInterrupt:
pass # Just continue...
# Start the main function (this is a script)
main()