mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 03:44:55 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			617 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			617 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
# 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
 |