home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / C / Applications / Python 1.3 / source code / Demo / tkinter / guido / ManPage.py < prev    next >
Encoding:
Python Source  |  1995-12-17  |  5.6 KB  |  220 lines  |  [TEXT/R*ch]

  1. # Widget to display a man page
  2.  
  3. import regex
  4. from Tkinter import *
  5. from ScrolledText import ScrolledText
  6.  
  7. # XXX These fonts may have to be changed to match your system
  8. BOLDFONT = '*-Courier-Bold-R-Normal-*-120-*'
  9. ITALICFONT = '*-Courier-Medium-O-Normal-*-120-*'
  10.  
  11. # XXX Recognizing footers is system dependent
  12. # (This one works for IRIX 5.2 and Solaris 2.2)
  13. footerprog = regex.compile(
  14.     '^     Page [1-9][0-9]*[ \t]+\|^.*Last change:.*[1-9][0-9]*\n')
  15. emptyprog = regex.compile('^[ \t]*\n')
  16. ulprog = regex.compile('^[ \t]*[Xv!_][Xv!_ \t]*\n')
  17.  
  18. # Basic Man Page class -- does not disable editing
  19. class EditableManPage(ScrolledText):
  20.  
  21.     # Initialize instance
  22.     def __init__(self, master=None, cnf={}):
  23.         # Initialize base class
  24.         ScrolledText.__init__(self, master, cnf)
  25.  
  26.         # Define tags for formatting styles
  27.         self.tag_config('X', {'underline': 1})
  28.         self.tag_config('!', {'font': BOLDFONT})
  29.         self.tag_config('_', {'font': ITALICFONT})
  30.  
  31.         # Set state to idle
  32.         self.fp = None
  33.         self.lineno = 0
  34.  
  35.     # Test whether we are busy parsing a file
  36.     def busy(self):
  37.         return self.fp != None
  38.  
  39.     # Ensure we're not busy
  40.     def kill(self):
  41.         if self.busy():
  42.             self._endparser()
  43.  
  44.     # Parse a file, in the background
  45.     def asyncparsefile(self, fp):
  46.         self._startparser(fp)
  47.         self.tk.createfilehandler(fp, tkinter.READABLE,
  48.                       self._filehandler)
  49.  
  50.     parsefile = asyncparsefile    # Alias
  51.  
  52.     # I/O handler used by background parsing
  53.     def _filehandler(self, fp, mask):
  54.         nextline = self.fp.readline()
  55.         if not nextline:
  56.             self._endparser()
  57.             return
  58.         self._parseline(nextline)
  59.  
  60.     # Parse a file, now (cannot be aborted)
  61.     def syncparsefile(self, fp):
  62.         from select import select
  63.         def avail(fp=fp, tout=0.0, select=select):
  64.             return select([fp], [], [], tout)[0]
  65.         height = self.getint(self['height'])
  66.         self._startparser(fp)
  67.         while 1:
  68.             nextline = fp.readline()
  69.             if not nextline:
  70.                 break
  71.             self._parseline(nextline)
  72.         self._endparser()
  73.  
  74.     # Initialize parsing from a particular file -- must not be busy
  75.     def _startparser(self, fp):
  76.         if self.busy():
  77.             raise RuntimeError, 'startparser: still busy'
  78.         fp.fileno()        # Test for file-ness
  79.         self.fp = fp
  80.         self.lineno = 0
  81.         self.ok = 0
  82.         self.empty = 0
  83.         self.buffer = None
  84.         savestate = self['state']
  85.         self['state'] = 'normal'
  86.         self.delete('1.0', 'end')
  87.         self['state'] = savestate
  88.  
  89.     # End parsing -- must be busy, need not be at EOF
  90.     def _endparser(self):
  91.         if not self.busy():
  92.             raise RuntimeError, 'endparser: not busy'
  93.         if self.buffer:
  94.             self._parseline('')
  95.         try:
  96.             self.tk.deletefilehandler(self.fp)
  97.         except TclError, msg:
  98.             pass
  99.         self.fp.close()
  100.         self.fp = None
  101.         del self.ok, self.empty, self.buffer
  102.  
  103.     # Parse a single line
  104.     def _parseline(self, nextline):
  105.         if not self.buffer:
  106.             # Save this line -- we need one line read-ahead
  107.             self.buffer = nextline
  108.             return
  109.         if emptyprog.match(self.buffer) >= 0:
  110.             # Buffered line was empty -- set a flag
  111.             self.empty = 1
  112.             self.buffer = nextline
  113.             return
  114.         textline = self.buffer
  115.         if ulprog.match(nextline) >= 0:
  116.             # Next line is properties for buffered line
  117.             propline = nextline
  118.             self.buffer = None
  119.         else:
  120.             # Next line is read-ahead
  121.             propline = None
  122.             self.buffer = nextline
  123.         if not self.ok:
  124.             # First non blank line after footer must be header
  125.             # -- skip that too
  126.             self.ok = 1
  127.             self.empty = 0
  128.             return
  129.         if footerprog.match(textline) >= 0:
  130.             # Footer -- start skipping until next non-blank line
  131.             self.ok = 0
  132.             self.empty = 0
  133.             return
  134.         savestate = self['state']
  135.         self['state'] = 'normal'
  136.         if TkVersion >= 4.0:
  137.             self.mark_set('insert', 'end-1c')
  138.         else:
  139.             self.mark_set('insert', 'end')
  140.         if self.empty:
  141.             # One or more previous lines were empty
  142.             # -- insert one blank line in the text
  143.             self._insert_prop('\n')
  144.             self.lineno = self.lineno + 1
  145.             self.empty = 0
  146.         if not propline:
  147.             # No properties
  148.             self._insert_prop(textline)
  149.         else:
  150.             # Search for properties
  151.             p = ''
  152.             j = 0
  153.             for i in range(min(len(propline), len(textline))):
  154.                 if propline[i] != p:
  155.                     if j < i:
  156.                         self._insert_prop(textline[j:i], p)
  157.                         j = i
  158.                     p = propline[i]
  159.             self._insert_prop(textline[j:])
  160.         self.lineno = self.lineno + 1
  161.         self['state'] = savestate
  162.  
  163.     # Insert a string at the end, with at most one property (tag)
  164.     def _insert_prop(self, str, prop = ' '):
  165.         here = self.index(AtInsert())
  166.         self.insert(AtInsert(), str)
  167.         if TkVersion <= 4.0:
  168.             tags = self.tag_names(here)
  169.             for tag in tags:
  170.                 self.tag_remove(tag, here, AtInsert())
  171.         if prop != ' ':
  172.             self.tag_add(prop, here, AtInsert())
  173.  
  174. # Readonly Man Page class -- disables editing, otherwise the same
  175. class ReadonlyManPage(EditableManPage):
  176.  
  177.     # Initialize instance
  178.     def __init__(self, master=None, cnf={}):
  179.         EditableManPage.__init__(self, master,
  180.                      (cnf, {'state': 'disabled'}))
  181.  
  182. # Alias
  183. ManPage = ReadonlyManPage
  184.  
  185. # Test program.
  186. # usage: ManPage [manpage]; or ManPage [-f] file
  187. # -f means that the file is nroff -man output run through ul -i
  188. def test():
  189.     import os
  190.     import sys
  191.     # XXX This directory may be different on your system
  192.     MANDIR = '/usr/local/man/mann'
  193.     DEFAULTPAGE = 'Tcl'
  194.     formatted = 0
  195.     if sys.argv[1:] and sys.argv[1] == '-f':
  196.         formatted = 1
  197.         del sys.argv[1]
  198.     if sys.argv[1:]:
  199.         name = sys.argv[1]
  200.     else:
  201.         name = DEFAULTPAGE
  202.     if not formatted:
  203.         if name[-2:-1] != '.':
  204.             name = name + '.n'
  205.         name = os.path.join(MANDIR, name)
  206.     root = Tk()
  207.     root.minsize(1, 1)
  208.     manpage = ManPage(root, {'relief': 'sunken', 'bd': 2,
  209.                  Pack: {'expand': 1, 'fill': 'both'}})
  210.     if formatted:
  211.         fp = open(name, 'r')
  212.     else:
  213.         fp = os.popen('nroff -man %s | ul -i' % name, 'r')
  214.     manpage.parsefile(fp)
  215.     root.mainloop()
  216.  
  217. # Run the test program when called as a script
  218. if __name__ == '__main__':
  219.     test()
  220.