removing more stdwin users
This commit is contained in:
parent
ede8c6eea1
commit
dab6cb8f6d
|
@ -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.
|
|
@ -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()
|
|
@ -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)
|
|
@ -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()
|
|
@ -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()
|
|
@ -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)
|
||||
#
|
|
@ -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
|
|
@ -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
|
||||
#
|
|
@ -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
|
||||
#
|
|
@ -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
|
|
@ -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()
|
|
@ -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.'
|
|
@ -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()
|
|
@ -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()
|
|
@ -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.
|
|
@ -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()
|
|
@ -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)
|
|
@ -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()
|
|
@ -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)
|
|
@ -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)
|
|
@ -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()
|
|
@ -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()
|
|
@ -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.
|
|
@ -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.
|
|
@ -1,2 +0,0 @@
|
|||
: ${ARCH}=`arch`
|
||||
exec /ufs/guido/bin/$ARCH/python ib.py ${1+"$@"}
|
|
@ -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()
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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.'
|
|
@ -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
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
Loading…
Reference in New Issue