blob: 8c3ad8c0fbd6e28b1bfd9ad9cd7f9e1cb868cb72 [file] [log] [blame]
Guido van Rossumf09b7701994-07-06 21:17:21 +00001# Widget to display a man page
2
3import regex
4from Tkinter import *
5from ScrolledText import ScrolledText
6
7# XXX These fonts may have to be changed to match your system
8BOLDFONT = '*-Courier-Bold-R-Normal-*-120-*'
9ITALICFONT = '*-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)
13footerprog = regex.compile(
14 '^ Page [1-9][0-9]*[ \t]+\|^.*Last change:.*[1-9][0-9]*\n')
15emptyprog = regex.compile('^[ \t]*\n')
16ulprog = regex.compile('^[ \t]*[Xv!_][Xv!_ \t]*\n')
17
18# Basic Man Page class -- does not disable editing
19class EditableManPage(ScrolledText):
20
21 def __init__(self, master=None, cnf={}):
22 # Initialize base class
23 ScrolledText.__init__(self, master, cnf)
24
25 # Define tags for formatting styles
Guido van Rossum72cb0201994-07-06 21:53:18 +000026 self.tag_config('bold', {'font': BOLDFONT})
27 self.tag_config('italic', {'font': ITALICFONT})
28 self.tag_config('underline', {'underline': 1})
Guido van Rossumf09b7701994-07-06 21:17:21 +000029
30 # Create mapping from characters to tags
31 self.tagmap = {
32 'X': 'bold',
33 '_': 'underline',
34 '!': 'italic',
35 }
36
37 # Parse nroff output piped through ul -i and append it to the
38 # text widget
39 def parsefile(self, fp):
Guido van Rossum72cb0201994-07-06 21:53:18 +000040 save_cursor = self['cursor']
41 self['cursor'] = 'watch'
42 self.update()
Guido van Rossumf09b7701994-07-06 21:17:21 +000043 ok = 0
44 empty = 0
45 nextline = None
46 while 1:
47 if nextline:
48 line = nextline
49 nextline = None
50 else:
51 line = fp.readline()
52 if not line:
53 break
54 if emptyprog.match(line) >= 0:
55 empty = 1
56 continue
57 nextline = fp.readline()
58 if nextline and ulprog.match(nextline) >= 0:
59 propline = nextline
60 nextline = None
61 else:
62 propline = ''
63 if not ok:
64 ok = 1
65 empty = 0
66 continue
67 if footerprog.match(line) >= 0:
68 ok = 0
69 empty = 0
70 continue
71 if empty:
72 self.insert_prop('\n')
73 empty = 0
74 p = ''
75 j = 0
76 for i in range(min(len(propline), len(line))):
77 if propline[i] != p:
78 if j < i:
79 self.insert_prop(line[j:i], p)
80 j = i
81 p = propline[i]
82 self.insert_prop(line[j:])
Guido van Rossum72cb0201994-07-06 21:53:18 +000083 self['cursor'] = save_cursor
Guido van Rossumf09b7701994-07-06 21:17:21 +000084
85 def insert_prop(self, str, prop = ' '):
Guido van Rossum72cb0201994-07-06 21:53:18 +000086 here = self.index(AtInsert())
87 self.insert(AtInsert(), str)
Guido van Rossumf09b7701994-07-06 21:17:21 +000088 for tag in self.tagmap.values():
Guido van Rossum72cb0201994-07-06 21:53:18 +000089 self.tag_remove(tag, here, AtInsert())
Guido van Rossumf09b7701994-07-06 21:17:21 +000090 if self.tagmap.has_key(prop):
Guido van Rossum72cb0201994-07-06 21:53:18 +000091 self.tag_add(self.tagmap[prop], here, AtInsert())
Guido van Rossumf09b7701994-07-06 21:17:21 +000092
93# Readonly Man Page class -- disables editing, otherwise the same
94class ReadonlyManPage(EditableManPage):
95
96 def __init__(self, master=None, cnf={}):
97 # Initialize base class
98 EditableManPage.__init__(self, master, cnf)
99
100 # Make the text readonly
Guido van Rossum72cb0201994-07-06 21:53:18 +0000101 self.bind('<Any-KeyPress>', self.modify_cb)
102 self.bind('<Return>', self.modify_cb)
103 self.bind('<BackSpace>', self.modify_cb)
104 self.bind('<Delete>', self.modify_cb)
105 self.bind('<Control-h>', self.modify_cb)
106 self.bind('<Control-d>', self.modify_cb)
107 self.bind('<Control-v>', self.modify_cb)
Guido van Rossumf09b7701994-07-06 21:17:21 +0000108
109 def modify_cb(self, e):
110 pass
111
112# Alias
113ManPage = ReadonlyManPage
114
115# Test program.
116# usage: ManPage [manpage]; or ManPage [-f] file
117# -f means that the file is nroff -man output run through ul -i
118def test():
119 import os
120 import sys
121 # XXX This directory may be different on your system
122 MANDIR = '/usr/local/man/mann'
123 DEFAULTPAGE = 'Tcl'
124 formatted = 0
125 if sys.argv[1:] and sys.argv[1] == '-f':
126 formatted = 1
127 del sys.argv[1]
128 if sys.argv[1:]:
129 name = sys.argv[1]
130 else:
131 name = DEFAULTPAGE
132 if not formatted:
133 if name[-2:-1] != '.':
134 name = name + '.n'
135 name = os.path.join(MANDIR, name)
136 root = Tk()
137 root.minsize(1, 1)
138 manpage = ManPage(root, {'relief': 'sunken', 'bd': 2,
139 Pack: {'expand': 1, 'fill': 'both'}})
140 if formatted:
141 fp = open(name, 'r')
142 else:
143 fp = os.popen('nroff -man %s | ul -i' % name, 'r')
144 manpage.parsefile(fp)
145 root.mainloop()
146
147# Run the test program when called as a script
148if __name__ == '__main__':
149 test()