home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pyos2bin.zip / Demo / tkinter / guido / ManPage.py < prev    next >
Text File  |  1996-07-30  |  6KB  |  221 lines

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