1 # Widget to display a man page
5 from Tkinter
import _tkinter
6 from ScrolledText
import ScrolledText
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-*'
12 # XXX Recognizing footers is system dependent
13 # (This one works for IRIX 5.2 and Solaris 2.2)
14 footerprog
= re
.compile(
15 '^ Page [1-9][0-9]*[ \t]+\|^.*Last change:.*[1-9][0-9]*\n')
16 emptyprog
= re
.compile('^[ \t]*\n')
17 ulprog
= re
.compile('^[ \t]*[Xv!_][Xv!_ \t]*\n')
19 # Basic Man Page class -- does not disable editing
20 class EditableManPage(ScrolledText
):
23 def __init__(self
, master
=None, **cnf
):
24 # Initialize base class
25 apply(ScrolledText
.__init
__, (self
, master
), cnf
)
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
)
36 # Test whether we are busy parsing a file
38 return self
.fp
!= None
40 # Ensure we're not busy
45 # Parse a file, in the background
46 def asyncparsefile(self
, fp
):
48 self
.tk
.createfilehandler(fp
, _tkinter
.READABLE
,
51 parsefile
= asyncparsefile
# Alias
53 # I/O handler used by background parsing
54 def _filehandler(self
, fp
, mask
):
55 nextline
= self
.fp
.readline()
59 self
._parseline
(nextline
)
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'])
69 nextline
= fp
.readline()
72 self
._parseline
(nextline
)
75 # Initialize parsing from a particular file -- must not be busy
76 def _startparser(self
, fp
):
78 raise RuntimeError, 'startparser: still busy'
79 fp
.fileno() # Test for file-ness
85 savestate
= self
['state']
86 self
['state'] = NORMAL
87 self
.delete('1.0', END
)
88 self
['state'] = savestate
90 # End parsing -- must be busy, need not be at EOF
93 raise RuntimeError, 'endparser: not busy'
97 self
.tk
.deletefilehandler(self
.fp
)
102 del self
.ok
, self
.empty
, self
.buffer
104 # Parse a single line
105 def _parseline(self
, nextline
):
107 # Save this line -- we need one line read-ahead
108 self
.buffer = nextline
110 if emptyprog
.match(self
.buffer) >= 0:
111 # Buffered line was empty -- set a flag
113 self
.buffer = nextline
115 textline
= self
.buffer
116 if ulprog
.match(nextline
) >= 0:
117 # Next line is properties for buffered line
121 # Next line is read-ahead
123 self
.buffer = nextline
125 # First non blank line after footer must be header
130 if footerprog
.match(textline
) >= 0:
131 # Footer -- start skipping until next non-blank line
135 savestate
= self
['state']
136 self
['state'] = NORMAL
138 self
.mark_set('insert', 'end-1c')
140 self
.mark_set('insert', END
)
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
149 self
._insert
_prop
(textline
)
151 # Search for properties
154 for i
in range(min(len(propline
), len(textline
))):
157 self
._insert
_prop
(textline
[j
:i
], p
)
160 self
._insert
_prop
(textline
[j
:])
161 self
.lineno
= self
.lineno
+ 1
162 self
['state'] = savestate
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)
169 tags
= self
.tag_names(here
)
171 self
.tag_remove(tag
, here
, AtInsert())
173 self
.tag_add(prop
, here
, AtInsert())
175 # Readonly Man Page class -- disables editing, otherwise the same
176 class ReadonlyManPage(EditableManPage
):
178 # Initialize instance
179 def __init__(self
, master
=None, **cnf
):
180 cnf
['state'] = DISABLED
181 apply(EditableManPage
.__init
__, (self
, master
), cnf
)
184 ManPage
= ReadonlyManPage
187 # usage: ManPage [manpage]; or ManPage [-f] file
188 # -f means that the file is nroff -man output run through ul -i
192 # XXX This directory may be different on your system
193 MANDIR
= '/usr/local/man/mann'
196 if sys
.argv
[1:] and sys
.argv
[1] == '-f':
204 if name
[-2:-1] != '.':
206 name
= os
.path
.join(MANDIR
, name
)
209 manpage
= ManPage(root
, relief
=SUNKEN
, borderwidth
=2)
210 manpage
.pack(expand
=1, fill
=BOTH
)
214 fp
= os
.popen('nroff -man %s | ul -i' % name
, 'r')
215 manpage
.parsefile(fp
)
218 # Run the test program when called as a script
219 if __name__
== '__main__':