1#!/bin/sh
2# This is a Tcl/Tk script to be interpreted by wish (Tk4.0 or better): \
3exec /usr/local/bin/wish8.6 "$0" "$@"
4
5
6##########################################################################
7# Version of TkInfo:
8set tki_version 2.11
9#
10# Authors:	Kennard White <kennard@ohm.eecs.berkeley.edu> (up to 0.7)
11# 		Axel Boldt <axelboldt@yahoo.com> (beginning with 0.8)
12# Copyright:	BSD-type license, see below
13#
14# A graphical browser for files in the GNU hypertext "info" format,
15# written in Tcl/Tk.
16#
17# Please see the "About" and "Info" file sections below.  (search for
18# "README" to find these sections quickly).  These explain much more
19# about what tkInfo is and what info files are, and gives references to
20# other programs and sources of info.  For information on the internals
21# of tkInfo, see the roadmap below.
22#
23# The program provides on-line help about itself: start it and hit `h'.
24#
25# This release should work with tcl7.4/tk4.0 or later.  tkInfo no
26# longer works with older versions (sorry).  tkInfo has gone through
27# several releases, but it is by no means complete.  Feel free to make
28# suggestions, or better yet, send me patch files.
29#
30# See below for copyright.  Basically you can re-distribute this any
31# way you like, just don't sue me and don't pretend you wrote tkInfo.
32#
33# Contributions and/or good ideas (some minor, some major) by Larry
34# Virden <lvirden@cas.org>, Bob Bagwill <bagwill@swe.ncsl.nist.gov>,
35# ??? <tlukka@snakemail.hut.fi>, Kurt Hornik
36# <hornik@neuro.tuwien.ac.at>, Hume Smith <850347s@dragon.acadiau.ca>,
37# Stephen Gildea <gildea@x.org>, Warren Jones <wjones@tc.fluke.COM>,
38# Robert Wilensky <wilensky@CS.Berkeley.EDU>, Frank Joachim Leitner
39# <ldvhp47@ldv.e-technik.tu-muenchen.de>, John Haxby <jch@pwd.hp.com>,
40# Craig Sanders <cas@taz.net.au>, Gerald S Williams
41# <gerald.s.williams@intel.com>, Peter Blackman <peter@pblackman.plus.com>.
42# Tom Phelps <phelps@CS.Berkeley.EDU> contributed the searching code, as
43# well as many other good ideas.
44# L J Bayuk <bayuk@mindspring.com> patched 2.5 for Tcl/Tk 8.4.
45#
46
47set tki_help_usage \
48{
49TkInfo: Stand-alone usage
50-------------------------
51
52    (requires the wish shell (Tk version 4.0 or better))
53
54When invoked with no arguments, tkInfo looks for an "info tree" (a
55collection of info files installed on your system) and displays the
56top level node.  On a well maintained system, you can get to every
57info file starting from this top level node.  Alternatively, you can
58specify the file and node you want to see on the command line.
59
60Usage: tkinfo [--help] [[-|+]headers] [[-|+]buttons]
61              [[-|+]scrollthrough] [[-|+]showdir] [[-|+]pagesep]
62              [-linklook type] [-highlight type] [-searchlook type]
63              [-geometry geom] [-display display] [-iconic]
64              [-dir dir1] [-dir dir2] ... [node]
65Options:
66 --help           Produces this help message.
67 -/+headers       Turns on/off display of the raw info node headers.
68 -/+buttons       Turns on/off display of the button row.
69 -/+balloons      Turns on/off balloonhelp for the buttons.
70 -/+scrollthrough Turns on/off going to successor when scrolling through end.
71 -/+showdir       Turns on/off showing the full pathname of the info file.
72 -/+pagesep       Turns on/off inserting page separators when scrolling.
73 -linklook        Specifies how to display xrefs and menu entries.  Must
74                  be one of "color", "font", or "underline".
75 -highlight       How to highlight links. Can be "color", "underline", or
76                  "inverse".
77 -searchlook      How to highlight the matches after searches. Can be "color",
78                  "underline", or "inverse".
79 -geometry        Geometry of the window. format: XxY+A+B or XxY or +A+B.
80                  X,Y specify size in characters, A,B give location in pixels.
81 -display         X display to use for the tkInfo window.
82 -iconic          Start the first window in iconic state.
83 -dir             Specifies a directory to search for info files, in addition
84                  to those contained in the INFOPATH environment variable.
85                  Several -dir options can be present; the directories will be
86                  searched before INFOPATH, in the order given.
87 node	          Specifies the node to visit initially. Possible formats:
88                    "(filename)nodename"  most general
89                    "(filename)"          equivalent to (filename)Top
90                    "filename"            equivalent to (filename).
91                  If filename is not absolute, the info directories (from
92                  INFOPATH and -dir) will be searched. If filename cannot be
93                  found, its lower case version will be tried.
94                  An alternative way to specify the node "(FILE)NODE" is
95                  with "-file FILE -node NODE".
96                  If no node is given, the default "(dir)Top" is used.
97
98Environment variables:
99 INFOPATH         A colon (`:') separated list of directories to search
100      	          for info files. More directories can be given with -dir
101                  option, above. If not set, TkInfo will try various
102                  standard directories that should be ok for most systems.
103 INFOSUFFIX	  A colon separated list of file suffixes to try when searching
104      	          for an info file. If not set, tkinfo will try the suffixes
105                  "", ".info", and "-info". In addition, tkinfo will
106                  always automatically try the suffixes .Z, .z, bz2, and .gz
107                  and uncompress transparently if necessary.
108}; set tki_custom \
109{
110How to customize tkInfo
111-----------------------
112
113The colors, fonts, and geometry of tkInfo can be customized using the
114standard X options database. A random example follows. You can either put
115(parts of) this in your .Xdefaults or .Xresources file or you can
116create a global file /usr/lib/X11/app-defaults/tkinfo that will apply
117to all users of your site. To have the new options take effect,
118restart your X server or use the program xrdb. More information about
119the X options mechanism is available from the X man page. Use tkman
120for reading man pages or you lose.
121
122Windows and Mac users can't do this, but they can change the "option"
123lines in the procedure tkiInit in the tkInfo script itself.
124
125
126=========== snip ==================================================
127! These tkInfo settings are annoying on purpose, just to demonstrate
128! what harm an evil mind can do.
129!
130! Specify the size in characters, the position in dots. You can also
131! leave the position out. Default geometry is 80x28 to fit on 640x480
132! screens.
133Tkinfo.geometry:                80x40+10+10
134! How many entries to keep in the info node history list.
135! Default is 20.
136Tkinfo*history:                 28
137! How many entries to keep in the history list for the prompt window.
138! Defaults to 35.
139Tkinfo*prompthistory:           20
140! Whether to jump to the successor node when attempting to scroll at the
141! bottom of a node. Can be 1 (default) or 0.
142Tkinfo*scrollthrough:           0
143! Whether to show the directory of the displayed node. Can be 0 (default)
144! or 1.
145Tkinfo*showdir:                 1
146! Whether to insert page separators when scrolling. Defaults to 1.
147Tkinfo*pagesep:                 0
148! How the links are displayed. Can be one of "color" (default),
149! "underline" (default on b&w terminals), or "font".
150Tkinfo*linklook:                font
151! The following is only used if linklook is set to "color"
152Tkinfo*linkcolor:               red
153! The following is only used if linklook is set to "font".
154Tkinfo*linkfont:                -*-courier-bold-o-normal-*-16-*-*-*-*-*-*-*
155! How a link is highlighted. Can be one of "inverse" (default), "underline",
156! or "color".
157Tkinfo*highlight:               color
158! Set this if highlight = color:
159Tkinfo*highlightcolor:          green
160! Set this if highlight = font:
161Tkinfo*highlightfont:           -*-courier-bold-o-normal-*-16-*-*-*-*-*-*-*
162! How to display the matches after a search. Can be "color", "font", or
163! "inverse" (default).
164Tkinfo*searchlook:              font
165Tkinfo*searchfont:              -*-courier-bold-o-normal-*-16-*-*-*-*-*-*-*
166Tkinfo*searchcolor:             violet
167! To switch off the lower row of buttons. (The default is "1" which means
168! display the buttons.)
169Tkinfo*showbuttons:             0
170! To switch off balloonhelp for the buttons. (Default is "1" which means
171! display balloonhelp.)
172Tkinfo*showballoons:            0
173! Delay after which balloonhelp appears, in thousands of a second.
174! Default: 400
175Tkinfo*balloondelay:            300
176! To switch off display of the full info file headers. (The default is "1"
177! which means show the info headers).
178Tkinfo*showheaders:             0
179! You can specify colors either as names (on my system, they are defined in
180! /usr/lib/X11/rgb.txt), or in the format #C0F1DD as a sequence of three
181! hex numbers giving the red-green-blue components.
182Tkinfo*background:              yellow
183Tkinfo*Text.background:         orange
184! The "trough" is the area where the scrollbar moves.
185Tkinfo*troughColor:             blue
186! This is for disabled menuentries and buttons.
187Tkinfo*disabledForeground:      #F00909
188Tkinfo*activeBackground:        blue
189Tkinfo*foreground:              black
190! Backgroundcolor of balloons (default: LightGoldenrodYellow)
191Tkinfo*balloonbackground:       violet
192! Font for the buttons and messages. You can find nice fonts with the program
193! xfontsel.
194Tkinfo*font:                    -*-helvetica-bold-r-normal-*-16-*-*-*-*-*-*-*
195Tkinfo*Text.font:               -*-courier-medium-r-normal-*-16-*-*-*-*-*-*-*
196! If you don't like that the window which owns the focus is highlighted:
197Tkinfo*highlightThickness:      0
198! Change the different mouse pointers here; the available cursornames
199! are contained in the file /usr/include/X11/cursorfont.h on my system.
200Tkinfo*linkcursor:              "double_arrow"
201Tkinfo*normcursor:              "fleur"
202Tkinfo*waitcursor:              "heart"
203! This one appears on Button-2:
204Tkinfo*handcursor:              "double_arrow"
205=========== snip ==================================================
206
207}; set tki_roadmap \
208{
209Roadmap to the tkInfo source code
210---------------------------------
211
212TkInfo is a Tcl/Tk script.  The following information is for people
213familiar with Tcl/Tk who want to hack on tkInfo.  You should read the
214tkinfo source along with this roadmap.
215
216We keep a global array of variables tki() to store things such as the
217current status of the user-toggable options, the current window, list
218of all windows, the mouse position at button-press events, and the
219extracted information of already parsed info files (including their
220full node text, see below). We also have a global array of variables
221for every toplevel window; the array has the same name as the window
222and is usually called wvars() through a call to upvar. We use wvars() to
223store displayed status messages, the name of the displayed node, the
224list of previously visited nodes, the string being searched for etc.
225
226The widget tree looks like this: the toplevel windows are called
227.tki1, .tki2, etc. .tki1.bar is the menubar with buttons .file, .node,
228.search, .options, and .help. The associated menus are called
229.tki1.bar.file.m and so on. The main text window is called
230.tki1.main.text and its scrollbar is .tki1.main.vsb. Then there is the
231the button row .tki1.buts with buttons .next, .prev, .up, and .last
232and the status line .tki1.s with filename window .tki1.s.filename and
233status message .tki1.s.status. If the user is prompted for an input,
234.tki1.s.filename contains the prompt and .tki1.s.input is the entry
235area.  Then there is the pop-up menu .tki1.transientmenu which appears
236on Button-3.
237
238
239tkInfo requires the following global variables:
240 tki		This is a huge array where all the loaded info-files
241		are stored.  It also contains some configuration state.
242		The contents of this is described below.
243 .tki##		Each toplevel info window has a global variable associated
244		with it.  The name of the variable is the same as the
245		toplevel window name, which is ".tki" followed by some number.
246 tkiEmbed	tkInfo can operate stand-alone (like the "info" program) or
247		embedded (part of your application).  Embedded mode is
248		used iff this variable exists.  When this file is
249		sourced in the stand-alone case, the argv options will be
250		parsed (see tkiBoot() below) and a new toplevel window
251	 	will be opened.
252
253
254tkInfo may be used in one of three modes: stand alone, embedded or
255as a server tool.  These modes are described below:
256
257Stand-alone
258	In this mode, the user directly invokes tkinfo, and directly
259	manipulates it to display the nodes of interest. This mode
260	requires that the shell script "tkinfo" be properly
261	configured, and that the info path be properly configured,
262	either by editing the default info path in "tkinfo"/tkiInit(),
263	or by the user's INFOPATH environment variable. The built-in
264	help contains additional information (command line arguments)
265	for this mode.
266
267Embedded
268	In this mode, your application will include tkinfo as part of
269	its distribution, and tkinfo will run within the same process
270	and the the same TCL interpreter as your application.  tkInfo
271	is written with this in mind, and avoids name space pollution.
272	In this mode, tkInfo doesn't do anything until the application
273	explicitly request an action by calling tkiWinShow(); normally
274	the application will do this in response to the user selecting
275	a "Help" button or pressing a "Help" key.
276
277	To use this mode, your application must set the global variable
278	``tkiEmbed'' to any value and then source "tkinfo" (the
279	auto-load facility may replace explicitly source'ing
280	"tkinfo", but ``tkiEmbed'' must be set before this
281	happens).
282
283	tkiAddInfoPaths() should be called by the application to let
284	tkInfo know where the application's info files are installed.
285	The application should call tkiWinShow() to display a window.
286	Also, the application may find tkiWinContextHelp() useful
287	for processing "Help" key bindings.
288
289Server Tool
290	From the user's perspective, this is very similar to the
291	Embedded mode, but the implementation is different.  In this
292	mode, tkInfo runs as in the stand-alone mode, but responds to
293	requests from other applications via Tk's "send" mechanism.
294	The application must rendezvous with tkInfo (locating the
295	existing server or starting a new server running) and makes
296	calls to tkiAddInfoPaths() and tkiWinShow() as in the embedded
297	case (but via "send").  The application may wish tkInfo to
298	dedicate a single window to the application, the "window tag"
299	feature of tkiWinShow() may be useful for this.
300
301
302The core structure of an info file is a {node}.  Each info file
303consists of a set of nodes separated by a magic character.  Each nodes
304consists of of a headerline and a body, which can contain a menu.
305There are also special nodes that contain control information used to
306reference "split" files and speed up access.  A node may be specified
307in one of several ways (called a {nodeSpec}):
308	(filename)nodename      Explicit.
309	nodename		The given node within the current file.
310	(filename)		The "Top" node of the file.
311If a filename can't be found, we try the lower case version; if a nodename
312can't be found we try case insensitive match.
313
314In the implementation below, the info format consists of {nodes} stored
315in files.  A given info file has three identifiers associated with it:
316 -	The {filename}, which is the name used either by the user to
317	reference a file, or by one info file to reference another.
318	Such a reference could be complete UNIX path name (either
319	absolute or relative), or may be a partial specification (see below).
320 -	The {filepath}, which is a valid UNIX path name to access the
321	file.  The filepath is derived from the filename.  If the filename
322	is already a valid path, no work needs be done.  Otherwise,
323	the filepath is formed by prepending a path prefix and appending
324	a file suffix.  These are defined by the INFOPATH and INFOSUFFIX
325	variables.
326 -	The {filekey}, which is an internal, auto-generated token associated
327	with each file.
328A typical (filename,filepath,filekey) would be
329(emacs-2,/usr/info/emacs-2.gz,fk3). This file has the info file called "emacs"
330as a parent.
331
332The global array "tki" contains the following elements about the
333already parsed files:
334  fileKeys-$fileName	The fileKeys for $fileName. If there are info files
335                        of the same name in different directories, they will
336                        get differnet fileKeys.
337  fileinfo-$fileKey	The fileinfo struct for $fileKey. Each fileinfo is
338			{ fileKey fileName filePath pntKey }
339                        pntKey is the filekey of the parent, or the empty
340                        list if there is no parent.
341  incore-$fileKey	Boolean 0/1; true if file has been loaded into core.
342  nodesinfo-$fileKey	A list of nodeinfo for every node in $fileKey.
343			Each nodeinfo is a list { idx node file up prev next }.
344                        Node, file, up, prev, next are the names given
345                        in the info node's first line.
346  nodesbody-$fileKey	A list of the textual body for every node in $fileKey.
347                        The nodes are given in the same order as in nodesinfo.
348  indirf-$fileKey	List of indirect-file-info for $fileKey.  Each
349			info is a list { indirFileKey byteOfs }.
350  indirn-$fileKey	List of indirect-node-info for $fileKey.  Each
351			info is a list { nodeName byteOfs fileKey }.
352  xrefinfo-$fileKey-$nodeIdx
353			A list of all cross reference
354			pointers within the node body's text. Every element
355                        has the form { idx toNode stpos endpos label }
356                        stpos and endpos give the position of the link in
357                        the text.
358  menuinfo-$fileKey-$nodeIdx
359			Contains information on all menu entries
360			within the node's menu text.  Consists of list of:
361			 { linecnt menucnt toNode nBeg nEnd menutxt }
362                        nBeg and nEnd give the positions of the menu entry
363                        in its line.
364
365Notes (some important, some not).
3661.	Because of the graphical system, there may be several parallel
367	info windows active.  These windows must operate independently.
368	Because of this, there can be no concept of the "current file"
369	or "current node" within the tkinfo core.  Rather, this information
370	must be maintained by the window.
3712.	Because of #1, we must maintain multiple files in core.  Currently
372	we never flush.
3733.	The background color used in tkiInit() is BISQUE1, from tk/defaults.h
3744.	The byte offsets in the indirect tables are not used as such;
375	this is because we parse the file when loaded.  However, they are
376	used to identify which indirect file the node is in.
3775.	The function tkiLoadFile() attempts to deal with compressed files.
378	Currently it uses "zcat" for .Z files, "bunzip2 -c" for .bz2
379        and "gunzip -c" for .z and .gz files.
380	If you have better suggestions, please let me know.
381
382
383Here are descriptions of the more important procedures:
384
385tkiInit
386   Initializes the default INFOPATH and other global variables such as
387   the default geometry, link color, cursor etc.  It also sets the
388   regular expression used for parsing info files by calling
389   _tkiNodeParseInit and sets up the builtin info pages by calling
390   _tkiBuiltinFile (which does its job by setting up the relevant
391   tki() variables so that it looks like tkInfo has actually parsed
392   the builtin info "files").
393
394tkiTimeStatus
395   takes a script as argument, executes it, and prints the time it
396   took on stdout. This can be used to profile tkinfo if the option
397   "Time Status" is enabled from the option menu. Several crucial
398   calls are wrapped in a tkiTimeStatus.
399
400tkiInfoWindow
401   Accepts the same arguments as tkinfo. It first parses the options
402   using topgetopt, processes them, and then calls tkiWinShow to
403   actually create the new window and display the node.
404
405tkiWinShow
406   The main entry point: takes the specification of an info node and a
407   window, creates that window if necessary, and displays the
408   node. This is also suitable for being called from other tcl
409   programs via send.
410
411tkiWinCreate
412   creates a toplevel window with all its subwindows. Also initializes
413   the winfo() variables for that window. Creates all bindings except
414   those for the main text window which are handled by _tkiWinBind.
415
416_tkiWinBind
417   Creates all the bindings for the text window and search entry
418   boxes. Many of these bindings are created automatically via
419   _tkiBindAccels from the accelerators of the various menu
420   entries. If you have bindings to add, here's the place.
421
422_tkiWinAction
423   The central manager of all actions that can be performed by the user
424   on a window, such as quitting, scrolling, searching, and moving to
425   other nodes. The actions themselves are actually handled by other
426   procedures.  This function is designed to be bound to various
427   events.
428
429_tkiWinPromptMap
430   Brings up the lower prompt area for searches etc.
431
432_tkiWinPromptOk
433   Is called when the user presses Enter in the lower prompt area. It
434   takes the appropriate action and unmaps the prompt area using
435   _tkiWinPromptUnmap.
436
437tkiWinDpy
438   Inserts a node into the current text window, complete with
439   tags. Updates the history and last lists. The actual parsing is
440   done in tkiNodeParseBody. Also updates the Next/Previous/Last
441   button bindings and enables/disables menu entries as appropriate
442   for the displayed node.
443
444tkiWinContextHelp
445   helper function for the case where tkinfo is embedded in a larger
446   application. The app can associate an infonode spec to every major
447   window, and this function will display the associated node in a new
448   tkinfo window.
449
450tkiFileGet
451   loads a file into memory and returns the filekey using tkiFileFind
452   and tkiFileLoad.
453
454tkiFileFind
455   returns the full filename of a partially specified info file using
456   the list of info directories and info suffixes and compression
457   suffixes.
458
459tkiFileLoad
460   loads an info file and parses its nodes using tkiFileParseNode in
461   order to fill up the respective entries in tki(). tkiFileParseNode
462   has to deal with tag tables (which describe where in a file a node
463   is located) and indirect tables which point to other info files.
464
465tkiGetNodeRef
466   locates an info node wherever it is; loads the info file if
467   necessary. Info files can be split; for example, emacs.info is only
468   a short table containing pointers to the info files emacs-1 to
469   emacs-29. This is called an "Indirect" table, and emacs.info is
470   called the parent of the other emacs info files. tkiGetNodeRef
471   deals with this mess transparently, calling itself recursively on a
472   child if necessary.
473
474tkiNodeParseBody
475   parses the body of a node to locate all crossreferences, and
476   returns a list of them and stores it in tki().
477
478tkiNodeParseMenu
479   parses the body of a node to locate all menu entries, and returns a
480   list of them and stores it in tki().
481
482_tkiWinManPage
483   displays a man page in a tkman window. Either starts a new tkman or
484   contacts an existing one. Communication is via the tcl send
485   mechanism. This does not work if you X server is insecure; use xdm
486   to get a secure session.
487
488_tkiBindAccels
489   a nice utility function to support accelerator keys in menus.
490
491searchboxSearch and searchboxNext
492   support for searching, regexp or normal, ready to be bound to
493   events.
494
495TextSearch and regexpTextSearch
496   used by the searchbox functions to locate all matching strings in a
497   text window and to apply a given tag to them.
498}
499
500#
501# README: You might want to customize "defInfoPath" below for your site,
502#	  just put your paths there and remove the others for faster
503#         startup.
504#	  If you feel there is a "standard" location not listed below,
505#	  please send me mail.
506#
507proc tkiInit { } {
508    global tki env auto_path tkiEmbed geometry
509
510    # No need to do this if we have been called before
511    if { [info exist tki(sn)] } return
512
513    set defInfoPath [list . \
514      /usr/info /usr/share/info /usr/local/info /usr/local/gnu/info \
515      /usr/local/emacs/info /usr/local/lib/emacs/info \
516      /usr/lib/xemacs/info /usr/local/lib/xemacs/info \
517      /usr/gnu/info \
518      ]
519
520    set defInfoSuffix [list .info -info ""]
521
522
523    option add *geometry                80x28    widgetDefault
524    option add *scrollthrough           1        widgetDefault
525    option add *showdir                 0        widgetDefault
526    option add *pagesep                 1        widgetDefault
527    option add *background		#d9d9d9  widgetDefault
528    option add *foreground		Black    widgetDefault
529    option add *history                 20       widgetDefault
530    option add *prompthistory           35       widgetDefault
531    option add *Text.background		#d9d9d9  widgetDefault
532    option add *Text.foreground		Black    widgetDefault
533    option add *font			"-*-helvetica-bold-r-normal-*-12-*-*-*-*-*-*-*" widgetDefault
534    option add *Text.font		"-*-courier-medium-r-normal-*-12-*-*-*-*-*-*-*" widgetDefault
535    option add *linklook                "color" widgetDefault
536    if { [info commands winfo] != "" } {
537	if { [winfo depth .] == 1 } {
538	    option add *linklook "underline" widgetDefault
539	}
540    }
541    option add *linkcolor               blue    widgetDefault
542    option add *linkfont                "-*-courier-bold-o-normal-*-12-*-*-*-*-*-*-*" widgetDefault
543    option add *highlight               inverse  widgetDefault
544    option add *highlightfont           "-*-courier-bold-o-normal-*-12-*-*-*-*-*-*-*" widgetDefault
545    option add *highlightcolor          violet  widgetDefault
546    option add *searchlook              inverse widgetDefault
547    option add *searchfont              "-*-courier-bold-o-normal-*-12-*-*-*-*-*-*-*" widgetDefault
548    option add *searchcolor             red widgetDefault
549    option add *showbuttons             "1" widgetDefault
550    option add *showballoons            "1" widgetDefault
551    option add *showheaders             "1" widgetDefault
552    option add *linkcursor		"hand2" widgetDefault
553    option add *normcursor		"left_ptr" widgetDefault
554    option add *waitcursor		"watch" widgetDefault
555    option add *handcursor              "sb_v_double_arrow" widgetDefault
556    option add *balloondelay            400
557    option add *balloonbackground       LightGoldenrodYellow
558
559    _tkiLoadAppDefaults {tkinfo Tkinfo TkInfo}
560
561    set tki(sn)			0
562    set tki(self)		[info script]
563    set tki(timestatusB)	0
564    set tki(iconic)             0
565    set tki(compresscat-Z)	"zcat"
566    set tki(compresscat-z)	"gunzip -c"
567    set tki(compresscat-gz)	"gunzip -c"
568    set tki(compresscat-bz2)    "bunzip2 -c"
569    set tki(rawHeadersB)	[option get . showheaders Showheaders]
570    set tki(showButtonsB)	[option get . showbuttons Showbuttons]
571    set tki(showBalloonsB)      [option get . showballoons Showballoons]
572    set tki(scrollThroughB)     [option get . scrollthrough Scrollthrough]
573    set tki(showDirB)           [option get . showdir Showdir]
574    set tki(pageSepB)           [option get . pagesep Pagesep]
575    set tki(nodeSep)		"\037"
576    set tki(nodeByteSep)	"\177"
577    set tki(topLevelNode)	"Top"
578    set tki(lastNodes)          ""
579    set tki(promptHistory)      ""
580    set tki(dirs)               ""
581    set tki(history)            ""
582    set tki(historyLength)      [option get . history History]
583    set tki(promptHistoryLength) [option get . prompthistory PromptHistory]
584
585    # The global $geometry is set by wish if -geometry was given on
586    # the command line. The command line option is eaten by wish and
587    # we will never see it.
588    set tki(geometry)           [option get . geometry Geometry]
589    if { [info exists geometry] } {
590        if { [string match "+*" $geometry] } {
591            regexp "\[^\\+\]*" $tki(geometry) dummy
592            set tki(geometry) $dummy$geometry
593        } else {
594            set tki(geometry) $geometry
595        }
596    }
597
598    set tki(linklook)		[option get . linklook Linklook]
599    set tki(linklookColor)	[option get . linkcolor Linkcolor]
600    set tki(linklookFont)	[option get . linkfont Linkfont]
601    set tki(highlight)		[option get . highlight Highlight]
602    set tki(highlightColor)	[option get . highlightcolor Highlightcolor]
603    set tki(highlightFont)	[option get . highlightfont Highlightfont]
604    set tki(searchlook)		[option get . searchlook Searchlook]
605    set tki(searchColor)	[option get . searchcolor Searchcolor]
606    set tki(searchFont)		[option get . searchfont Searchfont]
607    set tki(linkCursor)		[option get . linkcursor Linkcursor]
608    set tki(normCursor)		[option get . normcursor Normcursor]
609    set tki(waitCursor)		[option get . waitcursor Waitcursor]
610    set tki(handCursor)         [option get . handcursor Handcursor]
611    set tki(balloonBackground)  [option get . balloonbackground Balloonbackground]
612    set tki(balloonDelay)       [option get . balloondelay Balloondelay]
613
614    tkiBalloonInit
615
616    set tki(windows)		""
617    set tki(breakBindings) 0
618    set tki(curWindow)		""
619    set tki(lastDir)            ""
620
621    if [info exist env(INFOSUFFIX)] {
622	set tki(infoSuffix) [split $env(INFOSUFFIX) ":"]
623    } else {
624	set tki(infoSuffix) $defInfoSuffix
625    }
626
627    if [info exist env(INFOPATH)] {
628      tkiAddInfoPaths [split $env(INFOPATH) ":"]
629    } else {
630      tkiAddInfoPaths $defInfoPath
631    }
632
633    _tkiNodeParseInit
634    rename _tkiNodeParseInit ""
635
636    _tkiBuiltinFile
637    rename _tkiBuiltinFile ""
638
639    trace var tki(rawHeadersB) w "_tkiTraceOptionsCB"
640    trace var tki(showDirB) w "_tkiTraceOptionsCB"
641    trace var tki(pageSepB) w "_tkiTraceOptionsCB"
642    trace var tki(showButtonsB) w "_tkiTraceOptionsCB"
643    trace var tki(linklook) w "_tkiTraceOptionsCB"
644}
645
646proc _tkiTraceOptionsCB { n1 n2 op } {
647    tkiWinRefreshAll
648}
649
650proc tkiUninit { } {
651    global tki
652    # Must destroy all existing windows so that there is no trace
653    # on anything in $tki.  Note that the "Options" menu does direct
654    # traces on stuff in tki.
655    catch {eval destroy $tki(windows)}
656    catch {unset tki}
657}
658
659proc tkiReset { } {
660    global tk_version
661
662    if { [info exists tk_version]} {
663        tkiUninit
664        tkiInit
665    }
666}
667
668proc tkiStatus { msg {w ""} {permanent 1}} {
669    global tki
670    if { $w == "" } {
671        set w $tki(curWindow)
672    }
673    if { $w == "" } {
674        puts stdout "tkInfo: $msg"
675    } else {
676        upvar #0 $w wvars
677        if { $permanent == 1} {
678            set wvars(oldStatus) $msg
679        }
680        set wvars(statusPermanent) $permanent
681        $w.s.status conf -text $msg
682	# idletasks should be sufficient, but the geometry management
683	# apparently needs some X-events to make the redisplay occur
684	#update
685        update idletasks
686    }
687}
688proc tkiStatusUpdate { w } {
689    upvar #0 $w wvars
690    if {$wvars(statusPermanent) == 1} { return }
691    set wvars(statusPermanent) 1
692    $w.s.status conf -text $wvars(oldStatus)
693}
694proc tkiScrollUpdate { w } {
695    upvar #0 $w wvars
696    set wvars(scrollBackwardHitTop) 0
697    set wvars(scrollForwardHitBottom) 0
698}
699proc tkiWarning { msg } {
700    # Warnings always go to stderr
701    puts stderr "tkInfo Warning: $msg"
702}
703proc tkiFileWarning { fileSpec msg } {
704    global tki
705    if [info exist tki(fileinfo-$fileSpec)] {
706	set fileSpec [lindex $tki(fileinfo-$fileSpec) 2]
707    }
708    tkiWarning "$fileSpec: $msg"
709}
710proc tkiError { msg } {
711    global tki
712    if { $tki(curWindow) == "" } {
713        puts stdout "tkInfo Error: $msg"
714    } else {
715	set infowin $tki(curWindow)
716	upvar #0 $infowin wvars
717        $infowin conf -cursor  $tki(normCursor)
718        $infowin.main.text conf -cursor $tki(normCursor)
719        tkiBell
720        tkiStatus "Error: $msg" $infowin 0
721    }
722}
723
724#
725# This is complicated by the fact that "time" doesn't provide access to
726# the return value.  Thus "cnt" is used as follows:
727#	0 ==> Do once for timing, and repeat for return value (no side-affects)
728#	1 ==> Do once for timing&side-affects, empty return value
729#
730proc tkiTimeStatus { msg cnt args } {
731    global tki
732    if { $tki(timestatusB) } {
733	puts stdout "tkInfo time: $msg [lindex [time $args] 0] microseconds"
734	if { $cnt == 0 } {
735	    return [eval $args]
736	} else {
737	    return ""
738	}
739    } else {
740	return [eval $args]
741    }
742}
743
744# _tkiLoadAppDefaults classNameList ?priority?
745#   Searches for the app-default files corresponding to classNames in
746#   the order specified by X Toolkit Intrinsics (R5), and loads them with
747#   the priority specified (default: startupFile). From the Tcl FAQ.
748proc _tkiLoadAppDefaults {classNameList {priority startupFile}} {
749  set lang [_tkiEnvVal LANG]
750  if {[string length $lang] > 0} { set lang /$lang }
751  set filepath "\
752                /usr/lib/X11${lang}/app-defaults \
753                [split [_tkiEnvVal XFILESEARCHPATH] :] \
754                [_tkiEnvVal XAPPLRESDIR]${lang} \
755                [split [_tkiEnvVal XUSERFILESEARCHPATH] :] \
756                "
757  foreach i $classNameList {
758    foreach j $filepath {
759      if {[file exists $j/$i]} {
760        option readfile $j/$i $priority;
761      }
762    }
763  }
764}
765
766# _tkiEnvVal varName
767#   Looks up the environment variable named $varName and returns its value
768#   OR {} if it does not exist
769proc _tkiEnvVal varName {
770    global env
771    if {[info exists env($varName)]} { return $env($varName) }
772}
773
774#
775# This proc is called once during initialization, and then destroyed.
776# (It is destroyed to save memory).
777# Currently we fake all the appropriate table entires to create a "builtin"
778# file.  It might be easier, however, to just pass one large text string
779# into the parser and have it be dealt with like any other file.
780#
781
782proc _tkiBuiltinFile { } {
783    global tki tki_help_usage tki_roadmap tki_version tki_custom
784
785    set fileKey			builtin
786    set tki(fileKeys-$fileKey)	[list $fileKey]
787    set tki(fileinfo-$fileKey)	[list $fileKey $fileKey $fileKey ""]
788    set tki(incore-$fileKey)	1
789    set tki(nodesinfo-$fileKey) ""
790    set tki(nodesbody-$fileKey) ""
791
792
793    tkiFileParseNode $fileKey "
794File: builtin, Node: Top, Up: (dir)Top, Next: About
795
796TkInfo
797======
798
799TkInfo is a browser for files in the info documentation format, such
800as the very file you are reading right now. If you need help on using
801tkInfo, try selecting \"Quick Help\" or \"Info\" below. Select an item
802by moving the mouse over the highlighted text and pressing the left or
803middle mouse button.
804
805* Menu:
806
807* About::         Which version of tkInfo you use, who wrote it, and when.
808* Info::          The structure of Info files.
809* Quick Help::    What the keys and mouse buttons do.
810* Usage Tips::    How to use tkInfo efficiently.
811* Command Line::  Telling tkInfo where to search for info files, and more.
812* Customization:: Changing tkInfo's window size, fonts, and default behaviors.
813* Source::        Hacking on tkInfo and embedding it into other programs.
814* Copyright::     TkInfo is free. See here for more information.
815"
816
817#VERSION README
818    tkiFileParseNode $fileKey "
819File: builtin, Node: About, Up: Top, Next: Info, Prev: Top
820
821About tkInfo
822============
823
824This is tkInfo version $tki_version.
825TkInfo is a browser for documentation in the info file format.
826
827The versions of tkInfo up to 0.7-beta were written by Kennard White
828(kennard@ohm.eecs.Berkeley.EDU).  You can obtain the tkInfo
829distribution up to version 0.7-beta by anonymous ftp from:
830
831	ftp://ptolemy.eecs.berkeley.edu/pub/misc
832
833Axel Boldt (axelboldt@yahoo.com) adapted tkInfo in 1997 for newer
834tcl/tk versions and added some features. The versions 0.8 and later
835can be gotten from http://math-www.uni-paderborn.de/~axel/tkinfo/
836
837Please report any and all problems, fixes, and suggestions to
838axelboldt@yahoo.com.
839
840TkInfo may be freely modified and distributed; for details, *note
841Copyright::.
842
843    "
844
845    tkiFileParseNode $fileKey {
846File: builtin, Node: Info, Up: Top, Prev: About, Next: Quick Help
847
848Info Files
849==========
850
851
852tkInfo is a browser for "info" files, a file format that supports a
853robust hypertext system which is ideal for on-line help.
854
855Each info file consists of several "nodes", units of information that
856can contain crossreferences to other nodes. TkInfo displays one node
857per window at a time, and highlights the crossreferences.
858
859The entry point and top most node of an info file is usually called
860"Top" and contains the table of contents for the info file. Many
861nodes, including Top, contain menus pointing to subnodes, thus
862creating a tree of nodes. The subnodes specify their parent as their
863"up node". Furthermore, most nodes specify a "next node" and a
864"previous node" on the same level, and this yields a convenient way to
865traverse the tree.
866
867The top-level info file is called "dir" and contains only a single
868node "Top" which is a directory listing all the other info files on
869your system. This is where tkInfo starts out by default.
870
871GNU programs such as the editor emacs, the compiler gcc and the shell
872bash are documented in the texinfo format, which can be transformed
873into info files using the makeinfo program. It is also possible to
874print out high quality hardcopies from texinfo sources via the TeX
875system.
876
877}
878
879    tkiFileParseNode $fileKey {
880File: builtin, Node: Quick Help, Up: Top, Prev: Info, Next: Usage Tips
881
882tkInfo Quick Help
883=================
884
885The name of the current info node is given in the bottom left. Links
886to other nodes are highlighted.
887
888
889Mouse operations
890----------------
891
892Left click on link or button    Show node in current window.
893Middle click on link or button  Show node in new window.
894Middle button drag              Scroll.
895Right click on link or button   Show node in new window; future right clicks in
896                                current window will send output to that window.
897Right click elsewhere           Pop up menu with often used commands.
898
899
900Displaying other nodes
901----------------------
902
903n       Move to the "next" node of this node.
904p       Move to the "previous" node of this node.
905u       Move "up" from this node.
906l       Move back to the "last" node you were at, stack based.
907t       Move to current info file's "top" node, with the table of contents.
908d       Move to the "directory" node which lists all installed info files.
909],[     Move to logical successor (resp. predecessor) of this node.
9101-9     Move to first, second, etc, item in node's menu and show in current
911Tab     Mark next link. Shift-Tab marks previous link.
912Enter   Move to marked link. Ctrl-Enter shows node in new window.
913        window. Ctrl-1 - Ctrl-9 shows node in a new window.
914m,f     Enter beginning of a menu entry (resp. crossreference) to move to.
915        If several links match, then the first currently visible one wins.
916        Case does not matter. Crsr-Up recalls previous inputs.
917g,(     Enter file or node name to move to. Crsr-Up recalls previous inputs.
918        Syntax: NODENAME or (FILENAME) or (FILENAME)NODENAME
919
920
921Searching
922---------
923
924i       Look up a substring in current info file's indices and node names.
925,       Continue previous index lookup.
926s,/     Search for text in current file literally (resp. by grep-style
927        regular expression, using the special characters .*+?^$[]()|\ ).
928        At the end of the file, search will wrap around to the beginning.
929        Ctrl-g aborts; Crsr-Up recalls previous search strings.
930Ctrl-s  Continue previous search forward.
931r,\     Search backwards, literally resp. by regular expression.
932Ctrl-r  Continue previous search backward.
933
934
935Scrolling
936---------
937
938b, HOME, <			Jump to the beginning of the node.
939e, END,  >			Jump to the end of the node.
940SPACE, Ctrl-f, Ctrl-v, PgDn	Scroll down one page. If at end of node,
941                                jump to logical successor node.
942DEL, Ctrl-b, Alt-v, PgUp        Scroll up one page. If at beginning
943                                of node, jump to logical predecessor node.
944Crsr DOWN, j, Ctrl-n		Scroll down one line.
945Crsr UP, k, Ctrl-p		Scroll up one line.
946Ctrl-m				Jump to beginning of current node's menu.
947
948
949Miscellaneous
950-------------
951
952?       Show this quick help message.
953h       Show builtin tkinfo documentation.
954M       Show manual page using tkman.  Uses selection or prompts.
955A       Show unix apropos using tkman. Uses selection or prompts.
956!       Issue tcl command, results printed on stdout.
957c       Close the current window.
958q       Quit the tkInfo program.
959
960You can access a menu from the menubar by holding down ALT and pressing
961the underlined letter. Get rid of posted menus with ESC.
962
963
964Type "u" now in order to go up from this node and obtain more
965information on tkInfo and the info system in general, or type "n" to
966go to the next node with usage tips for tkInfo, or close this help
967window with "c".
968    }
969
970
971    tkiFileParseNode $fileKey {
972File: builtin, Node: Usage Tips, Up: Top, Prev: Quick Help, Next: Command Line
973
974Usage Tips for TkInfo
975=====================
976
977
978Next, Previous, Last and Back
979-----------------------------
980
981The most important thing to understand is the function of the "Next"
982and "Previous" buttons. They have nothing to do with netscape's "Back"
983and "Forward" buttons. Rather, every info node specifies a "next node"
984and a "previous node" in its first line, and the "Next" and "Previous"
985buttons simply jump there. Most info files are organized in such a
986fashion that the next node is on the same hierarchical level as the
987current one, so that all menu entries of the current node are skipped
988when you click on "Next". Think of nodes as pages of a book: "Next"
989jumps from one section's title page to the next section's title page
990(which need not be the immediately following page). That's why
991continually hitting "Next" will generally NOT visit all the nodes of
992an info file in order. If you want to do that, simply keep hitting
993Space, or choose "Logical Successor" from the popup menu that's bound
994to the right mouse button.
995
996If you want to go to wherever you were before, use the "Last" button
997or the History menu. This is the functional equivalent to netscape's
998"Back".
999
1000
1001Redirection Windows
1002-------------------
1003
1004If you do not want to visit all nodes in order, you have to navigate
1005through the menus by clicking on entries. This can become confusing,
1006unless you make use of the middle and right mouse buttons. Clicking on
1007a link with the middle button will bring up that node in a new
1008window. This comes handy when you quickly want to check out a cross
1009reference that would only distract if brought up in the main window.
1010
1011Clicking on a link with the right button creates a "redirection
1012window" for the current window and displays the node there. If you
1013continue to use the right button in the current window, the output
1014will also be sent to that redirection window. Every window (even
1015redirection windows) can have one redirection window associated with
1016them in this manner. This is nice because it avoids too many tkinfo
1017windows cluttering up your desktop and it is useful when browsing
1018through large menus: I usually keep the menu visible in one main
1019window and explore the interesting menu entries in its redirection
1020window, which I place right next to the main window.
1021
1022
1023Top and Dir
1024-----------
1025
1026"Top" is the topmost node of the current info file and will usually
1027contain the table of contents. The "Top" nodes of bigger info files
1028often contain a detailed node listing following the menu of immediate
1029subnodes. This way, you can access every node of the info file with a
1030single click and there's no need to navigate the hierarchy at all.
1031
1032"Dir" is the toplevel info file which contains a listing and short
1033description of all the installed info files on the system. Some
1034systems have several Dir files because they store their info files in
1035several directories; if tkInfo knows about these, they will show up
1036under the Directories Menu entry.
1037
1038
1039Working quickly, with and without the mouse
1040-------------------------------------------
1041
1042Don't forget that you can scroll the current window by dragging with
1043the middle button. I think this is more comfortable than using the
1044scrollbar. The mouse bindings are designed so that most functions of
1045the program can be used easily with one hand on the mouse, and without
1046much need for the other hand or for mouse movements (right-click
1047brings up a popup menu).
1048
1049If you are more of a keyboard person, get used to TAB, Shift-Tab, and
1050RET to walk through a menu and to select a link. Also, selecting the
1051forth menu entry for instance is most quickly done by simply hitting
1052`4'.
1053
1054If you like working efficiently, you should try TkMan for reading unix
1055manpages.
1056
1057
1058Searching
1059---------
1060
1061When prompted for a string in the input box below, remember that you
1062can recall the previous inputs with the cursor UP key. If a search
1063takes too long, you can interrupt it with Ctrl-g. The search will
1064start at the beginning of the current node and will wrap around to the
1065beginning of the info file if you continue to hit Ctrl-s. You can
1066always jump back to the last match with Ctrl-r.
1067
1068Instead of doing a full-text search of the whole info file with `s',
1069it's usually better to start with an index lookup (`i'), which will
1070try to locate the term in the index nodes and then jump to the
1071relevant nodes explaining the term. If you want to browse through the
1072full index, hit `i RET'.
1073
1074
1075Printing
1076--------
1077
1078If you feel the urge to print out an info file, don't. Rather, get
1079your hands on the corresponding texinfo source and print that one
1080using the TeX system. The output is much prettier. Info files are not
1081meant to be printed, and that's why tkInfo doesn't have a print
1082option.
1083
1084
1085Info Tutorial
1086-------------
1087
1088There is a GNU program called "info" that is similar to tkInfo, but
1089completely text based.  A tutorial info file written for this program
1090is available on most systems.  This tutorial is useful if you want to
1091learn tkInfo's accelerator keys, since the keybindings of tkInfo and
1092info are almost identical. It will also tell you more about info files
1093in general. To see this tutorial, select the menu entry below.
1094
1095* Menu:
1096* Plain Info Tutorial: (info)Help.
1097    }
1098
1099    tkiFileParseNode $fileKey "
1100File: builtin, Node: Command Line, Up: Top, Next: Customization, Prev: Usage Tips
1101$tki_help_usage"
1102
1103    tkiFileParseNode $fileKey "
1104File: builtin, Node: Customization, Up: Top, Next: Source, Prev: Command Line
1105$tki_custom"
1106
1107    tkiFileParseNode $fileKey "
1108File: builtin, Node: Source, Up: Top, Next: Copyright, Prev: Customization
1109$tki_roadmap"
1110
1111#README
1112    tkiFileParseNode $fileKey {
1113File: builtin, Node: Copyright, Up: Top, Prev: Source
1114
1115TkInfo's Copyright
1116==================
1117
1118This copyright applies to the tkInfo system only.  If tkInfo is
1119embedded within a larger system, that system will most likely have
1120a different copyright.
1121
1122Sorry this is so long.  Basically, do whatever you want with this
1123software, just don't sue me and don't pretend you wrote it -- kennard.
1124
1125The parts I added are Copyright (c) 1997-2004 Axel Boldt and are covered
1126by the same license below -- Axel.
1127
1128Copyright (c) 1993 The Regents of the University of California.
1129All rights reserved.
1130
1131Permission is hereby granted, without written agreement and without
1132license or royalty fees, to use, copy, modify, and distribute this
1133software and its documentation for any purpose, provided that the above
1134copyright notice and the following two paragraphs appear in all copies
1135of this software.
1136
1137IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
1138FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
1139ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
1140THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
1141SUCH DAMAGE.
1142
1143THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
1144INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1145MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
1146PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
1147CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
1148ENHANCEMENTS, OR MODIFICATIONS.
1149    }
1150
1151# Does this save memory? Who knows, it can't hurt.
1152set tki_custom ""
1153set tki_roadmap ""
1154
1155}
1156
1157
1158#
1159# Do stand-alone help window
1160# The -node option is for compatibility to the info program only.
1161#
1162
1163proc tkiInfoWindow { args } {
1164    global  tki_help_usage tki tk_version
1165
1166    set w ""
1167    set nodeSpec ""
1168    set fileSpec ""
1169    set fileSpec2 ""
1170    set dirList ""
1171    set linklook ""
1172    set highlight ""
1173    set searchlook ""
1174    set headersB -1
1175    set buttonsB -1
1176    set balloonsB -1
1177    set scrollthroughB -1
1178    set showDirB -1
1179    set pageSepB -1
1180    set help -1
1181    set initialIconic -1
1182    set opt_list {
1183        { "window" w }
1184        { "dir" dirList append }
1185        { "file" fileSpec }
1186        { "headers" headersB bool }
1187        { "buttons" buttonsB bool }
1188        { "balloons" balloonsB bool }
1189        { "help" help bool }
1190        { "h" help bool }
1191        { "-help" help bool }
1192        { "iconic" initialIconic bool }
1193        { "scrollthrough" scrollthroughB bool }
1194        { "showdir" showDirB bool }
1195        { "pagesep" pageSepB bool }
1196        { "linklook" linklook }
1197        { "searchlook" searchlook }
1198        { "highlight" highlight }
1199        { "infofile" fileSpec2 }
1200        { "node" nodeSpec append }
1201    }
1202
1203    set args [topgetopt $opt_list $args]
1204
1205    if { $help != -1 } {puts $tki_help_usage; exit}
1206    set tki_help_usage ""
1207
1208    if { ![info exists tk_version] } {
1209        puts "TkInfo needs the X Window system."
1210        exit
1211    }
1212
1213    if { ![info exist tki] } { tkiInit }
1214
1215    if { $dirList != "" } {
1216        tkiAddInfoPaths $dirList
1217    }
1218
1219    if { $linklook != "" }	{ set tki(linklook) $linklook }
1220    if { $searchlook != "" }	{ set tki(searchlook) $searchlook }
1221    if { $highlight != "" }	{ set tki(highlight) $highlight }
1222    if { $initialIconic != -1 } { set tki(iconic) $initialIconic }
1223    if { $headersB != -1 }	{ set tki(rawHeadersB) $headersB }
1224    if { $buttonsB != -1 }	{ set tki(showButtonsB) $buttonsB }
1225    if { $balloonsB != -1 }     { set tki(showBalloonsB) $balloonsB }
1226    if { $scrollthroughB != -1 } { set tki(scrollThroughB) $scrollthroughB }
1227    if { $showDirB != -1 }      { set tki(showDirB) $showDirB }
1228    if { $pageSepB != -1 }      { set tki(pageSepB) $pageSepB }
1229    if { $fileSpec == "" }	{ set fileSpec $fileSpec2 }
1230
1231    if { $args != "" } {
1232	eval lappend nodeSpec $args
1233    }
1234    if { [llength $nodeSpec] > 1 } {
1235	error "tkiInfoWindow: Only one node may be specified"
1236    }
1237    set nodeSpec [lindex $nodeSpec 0]
1238    if { [tkiFileIsAbsolute $fileSpec] } {
1239            tkiAddInfoPaths [file dirname $fileSpec]
1240    }
1241
1242    set result [tkiWinShow $nodeSpec $fileSpec $w]
1243    set noderef [lindex $result 0]
1244    set win [lindex $result 1]
1245    if {$noderef == ""} {
1246        tkiWinShow {(builtin)Top} {} $win
1247        tkiStatus "Error: requested info file not found. Showing tkinfo docs instead." $win 0
1248    } else {
1249        tkiStatus "Welcome to tkInfo!    Hit `?' for help." $win 0
1250    }
1251    return ""
1252}
1253
1254#
1255# We are operating in one of two modes:
1256#   1)  Stand-alone.  Popup an initial window, filling it according to argv.
1257#	Kill the stupid "." window.
1258#   2)	Embedded within a larger application.  Don't do anything automatically;
1259#	instead, let that application's startup script handle things.
1260#
1261# We are operating in embedded mode iff the global tkiEmbed exists.
1262#
1263
1264proc tkiBoot { } {
1265    global argv tki tkiEmbed tk_version
1266
1267    if { [info exists tkiEmbed] } return
1268
1269    # We need the following 'if' only for the -help command line option;
1270    # apparently, Tk is not loaded if -help is given to wish...
1271    if { [info exists tk_version]} {
1272        wm withdraw .
1273    }
1274#    if { [lindex $argv 0] != "" && [file isfile [lindex $argv 0]] } {
1275#	# Some wishs pass the filename as argv[0].  Kill it off.
1276#	set argv [lreplace $argv 0 0]
1277#    }
1278
1279    eval tkiInfoWindow $argv
1280}
1281
1282##########################################################################
1283# The following material was formerly contained in the file tkiwin.tcl
1284#
1285#  In the function names below, I use the abbreviations:
1286#  	Show	Display a node specified by a nodeSpec and optional fileSpec.
1287#  		This provides the external interface, and requires
1288#  		processing by the tkicore functions to retrieve the
1289#  		actual data for display.
1290#  	Dpy	Display a node specified by a fileKey and an internal
1291#  		representation of the node.  This is an internal interface.
1292#
1293
1294
1295
1296#
1297# Support calling a running tkman (or starting one up). Adapted from
1298# remote.tcl that comes with tkman. This supports both regular man pages
1299# and apropos searches (if $apropos == 1).
1300#
1301
1302proc _tkiWinManPage { w manpage {apropos 0}} {
1303   global tki
1304
1305   if {[set found [lsearch [winfo interps] tkman*]]==-1} {
1306      # if TkMan doesn't already exist, start one up
1307      if {[catch {exec tkman &}]} {tkiError "Tkman cannot be started"; return}
1308
1309      # wait for it to be registered
1310      for {set found -1} {$found==-1} {after 200} {
1311         set found [lsearch [winfo interps] tkman*]
1312      }
1313
1314      # check whether server is secure
1315      catch {send tkman set manx(init)} error
1316      if {[string match "*insecure*" $error]} {
1317          tkiError \
1318"Cannot communicate with tkman:
1319X server is insecure.
1320Use xauth or xdm."
1321          return
1322      }
1323
1324      # wait for it to initialize
1325      for {set ready 0} {!$ready} {after 200} {
1326         catch {if {[send tkman set manx(init)]=="1"} {set ready 1}}
1327      }
1328   }
1329   set tkman [lindex [winfo interps] $found]
1330
1331   # .man is the main window, guaranteed to exist
1332   send $tkman raise .man
1333   if { $apropos } {
1334       send $tkman manApropos $manpage
1335   } else {
1336       send $tkman manShowMan $manpage
1337   }
1338
1339   return
1340}
1341
1342#
1343# Various functions for manipulating the "prompting" window.  This
1344# is the entry widget at the bottom of the info window used for entering
1345# node names and search text.
1346#
1347
1348#
1349# Create the prompt window, and enter the text "extra" into it.
1350#
1351proc _tkiWinPromptMap { w mode promptstring {extra ""} } {
1352    upvar #0 $w wvars
1353
1354    set wvars(promptmode) $mode
1355    set wvars(promptHistoryIdx) -1
1356    set dd $w.s
1357    $dd.input delete 0 end
1358    $dd.input insert end $extra
1359    $dd.filename conf -text $promptstring
1360    pack forget $dd.status
1361    pack $dd.input -after $dd.filename -side left -expand 1 -fill both
1362    switch $mode {
1363        search {
1364            pack $dd.regexp -after $dd.input -side left -fill y
1365            pack $dd.case -after $dd.regexp -side left -fill y
1366            pack $dd.back -after $dd.case -side left -fill y
1367        }
1368        manual {
1369            pack $dd.man -after $dd.input -side left -fill y
1370            pack $dd.apropos -after $dd.man -side left -fill y
1371        }
1372    }
1373    focus $dd.input
1374}
1375#
1376# Unmap the prompt window.
1377#
1378proc _tkiWinPromptUnmap { w } {
1379    upvar #0 $w wvars
1380    if { $wvars(promptmode) != "" } {
1381        set wvars(promptmode) ""
1382        set dd $w.s
1383        focus $w.main.text
1384        pack forget $dd.input
1385        pack forget $dd.regexp
1386        pack forget $dd.case
1387        pack forget $dd.back
1388        pack forget $dd.apropos
1389        pack forget $dd.man
1390
1391        pack $dd.status -after $dd.filename -side left -fill x -expand 1
1392        $dd.filename conf -text $wvars(nodeSpec)
1393    }
1394}
1395
1396#
1397# add the specified text to the history list of the prompt window.
1398# Make sure that prompt history list contains no doubles and doesn't grow
1399# to long.
1400# Do nothing if text is empty.
1401#
1402
1403proc _tkiWinPromptHistoryAdd { w text mode } {
1404    global tki
1405    upvar #0 $w wvars
1406    if { $text == "" } {
1407        return
1408    }
1409
1410    set wvars(promptHistory) [linsert $wvars(promptHistory) 0 [list $mode $text]]
1411    for {set idx 1} {$idx < [llength $wvars(promptHistory)]} {incr idx} {
1412        if {[lindex $wvars(promptHistory) $idx] == [list $mode $text]} {
1413            set wvars(promptHistory) [lreplace $wvars(promptHistory) $idx $idx]
1414            break
1415        }
1416    }
1417    if { [llength $wvars(promptHistory)] == $tki(promptHistoryLength) } {
1418        set wvars(promptHistory) [lreplace $wvars(promptHistory) end end]
1419    }
1420}
1421
1422#
1423# scroll the text in the prompt window according to the prompt history list.
1424#
1425
1426proc _tkiWinPromptScroll { w dir } {
1427    upvar #0 $w wvars
1428
1429    if { $dir == "up" } {
1430        set length [llength $wvars(promptHistory)]
1431        for { set idx [expr $wvars(promptHistoryIdx) + 1] } { $idx < $length } {incr idx} {
1432            set entry [lindex $wvars(promptHistory) $idx]
1433            if { [lindex $entry 0] == $wvars(promptmode) } {
1434                set wvars(promptHistoryIdx) $idx
1435                $w.s.input del 0 end
1436                $w.s.input insert end [lindex $entry 1]
1437                return
1438            }
1439        }
1440    } else {
1441        for { set idx [expr $wvars(promptHistoryIdx) - 1] } { $idx >= 0 } { incr idx -1} {
1442            set entry [lindex $wvars(promptHistory) $idx]
1443            if { [lindex $entry 0] == $wvars(promptmode) } {
1444                set wvars(promptHistoryIdx) $idx
1445                $w.s.input del 0 end
1446                $w.s.input insert end [lindex $entry 1]
1447                return
1448            }
1449        }
1450        set wvars(promptHistoryIdx) -1
1451        $w.s.input del 0 end
1452    }
1453}
1454
1455
1456#
1457# This is called when <Return> is pressed in the "goto" text window.
1458# We could either be in a goto-node command, or a search, or an
1459# exec-tcl, or an indexlookup, or a manual command.
1460# We take the appropriate action and cleanup.
1461#
1462
1463proc _tkiWinPromptOk { w } {
1464    global tki
1465    upvar #0 $w wvars
1466    set dd $w.s
1467    set input [$dd.input get]
1468    if { $wvars(promptmode) != "search" && $wvars(promptmode) != "indexlookup" } {
1469        set input [string trim $input]
1470    }
1471    if { $input == ""} {
1472        if { $wvars(promptmode) == "search"} {
1473            set tki(curWindow) $w
1474            _tkiWinPromptUnmap $w
1475            if { $wvars(searchBackB) == "1" } {
1476                _tkiWinAction $w search backIncr
1477            } else {
1478                _tkiWinAction $w search forwIncr
1479            }
1480            return
1481        } elseif { $wvars(promptmode) != "indexlookup" } {
1482            _tkiWinPromptUnmap $w
1483            return
1484        }
1485    }
1486    _tkiWinPromptHistoryAdd $w $input $wvars(promptmode)
1487    set tw $w.main.text
1488    $tw conf -cursor $tki(waitCursor)
1489    $w conf -cursor $tki(waitCursor)
1490    switch $wvars(promptmode) {
1491      search {
1492          set tki(curWindow) $w
1493          _tkiWinPromptUnmap $w
1494          if { $wvars(searchBackB) } {
1495              set cnt [searchboxSearchBackw $input $wvars(searchRegexpB) \
1496                        $wvars(searchCaseB) searchkey $w]
1497          } else {
1498              set cnt [searchboxSearch $input $wvars(searchRegexpB) \
1499                        $wvars(searchCaseB) searchkey $w]
1500          }
1501          set wvars(searchStr) $input
1502      }
1503      goto {
1504          _tkiWinPromptUnmap $w
1505          set result [tkiWinShow $input $wvars(fileKey) $w]
1506          if { [lindex $result 0] == "" } {
1507              set wvars(gotoStr) $input
1508          } else {
1509              set wvars(gotoStr) ""
1510          }
1511      }
1512      indexlookup {
1513          _tkiWinPromptUnmap $w
1514          set infoFileKey $tki(infoFileKey-$wvars(fileKey))
1515          _tkiIndexEntries $w $wvars(fileKey) $infoFileKey $input
1516          if { $wvars(indexEntries) == "" } {
1517              if {$input == ""} {
1518                  tkiStatus "No index in this info file." $w 1
1519              } else {
1520                  tkiStatus "No index entries contain \"$input\"." $w 1
1521              }
1522          } else {
1523              _tkiWinAction $w indexnext
1524          }
1525      }
1526      tclcmd {
1527        if [catch {uplevel #0 $input} error] {
1528            puts stdout "Error: $error"
1529        } else {
1530            puts stdout [expr { $error == "" ? "Ok" : "$error" }]
1531        }
1532        _tkiWinPromptUnmap $w
1533      }
1534      manual {
1535          set tki(curWindow) $w
1536          _tkiWinPromptUnmap $w
1537          if { $wvars(manB) } {
1538              _tkiWinManPage $w $input
1539          } else {
1540              _tkiWinManPage $w $input 1
1541          }
1542      }
1543      menu {
1544          set toNode [_tkiFindRef $w $input 0]
1545          _tkiWinPromptUnmap $w
1546          if { $toNode == ""} {
1547              tkiStatus "No such menu entry!" $w 0
1548          } else {
1549              tkiWinShow $toNode $wvars(fileKey) $w
1550          }
1551      }
1552      xref {
1553          set toNode [_tkiFindRef $w $input 1]
1554          _tkiWinPromptUnmap $w
1555          if { $toNode == ""} {
1556              tkiStatus "No such crossreference!" $w 0
1557          } else {
1558              tkiWinShow $toNode $wvars(fileKey) $w
1559          }
1560      }
1561    }
1562    $tw conf -cursor $tki(normCursor)
1563    $w conf -cursor $tki(normCursor)
1564}
1565
1566proc _tkiWinPromptAbort { w } {
1567    upvar #0 $w wvars
1568    _tkiWinPromptUnmap $w
1569}
1570
1571#
1572# This updates the global $tki(geometry) variable to the size of the
1573# specified window.
1574#
1575
1576proc _tkiWinGetGeom { w } {
1577  global tki
1578  scan [wm geometry [winfo toplevel $w]] "%dx%d+%s"  x y leftover
1579  set tki(geometry) "${x}x$y"
1580}
1581
1582#
1583# returns the indices of the first visible character and the last
1584# visible character of the text widget $tw. Furthermore, it is
1585# determined if the first and last lines are wrapped.
1586#
1587proc _tkiWinVisibleInfo { tw } {
1588    set topindex [$tw index @0,0]
1589    if {[$tw bbox "$topindex linestart"] == "" } {
1590        set firstiswrapped 1
1591    } else {
1592        set firstiswrapped 0
1593    }
1594    scan [wm geometry [winfo toplevel $tw]] "%dx%d+%s"  columns lines leftover
1595    #This is so complicated because of possible wrapping.
1596    set charactergeom [$tw bbox $topindex]
1597    set xmiddlefirst [expr [lindex $charactergeom 0] +2]
1598    set ymiddlefirst [expr [lindex $charactergeom 1] +2]
1599    set characterheight [lindex $charactergeom 3]
1600
1601    set lastindex [$tw index "@${xmiddlefirst},[expr $ymiddlefirst + ($lines - 1) * $characterheight]"]
1602    if {[$tw bbox "$lastindex lineend"] != ""} {
1603        set lastiswrapped 0
1604        set lastonpageindex [$tw index "$lastindex lineend"]
1605    } else {
1606        set lastlineinfo [$tw dlineinfo $lastindex]
1607        if {$lastlineinfo == ""} {
1608            tkiError "Couldn't scan text widget information correctly."
1609            return
1610        }
1611        scan $lastlineinfo "%d %d %d %s" x y width leftover
1612        set lastonpageindex [$tw index "@[expr $x+$width -2],[expr $y+2]"]
1613        set lastiswrapped 1
1614    }
1615    return [list $topindex $lastonpageindex $firstiswrapped $lastiswrapped $columns $lines]
1616}
1617
1618#
1619# Add information about currently displayed node to the end of
1620# wvars(lastNodes), but only if it is different from
1621# (oldinfo,oldfileKey).  Return the result.
1622#
1623
1624proc _tkiLastInfo { w oldinfo oldfileKey} {
1625    upvar #0 $w wvars
1626
1627    set result $wvars(lastNodes)
1628
1629    if { $wvars(noLastInfoUpdate) == 1 } {
1630        set wvars(noLastInfoUpdate) 0
1631        return $result
1632    }
1633
1634    if { $wvars(fileKey) == "" } {
1635        return $result
1636    }
1637
1638    # We don't want doubles
1639    if { $wvars(fileKey) == $oldfileKey && $wvars(nodeinfo) == $oldinfo } {
1640        return $result
1641    }
1642
1643    # Get topline
1644    set topline [$w.main.text index @0,0]
1645
1646    lappend result [list $wvars(fileKey) [lindex $wvars(nodeinfo) 1] $topline $wvars(cursorInfo)]
1647    return $result
1648}
1649
1650#
1651# Return the node in the current node's menu whose label starts with
1652# $labelstart. We assume that the current node has a menu resp.
1653# crossreferences. If there are more than one matching node, the first
1654# currently visible one wins. Case does not matter.
1655# If xref is 1, look for crossreferences instead.
1656# Returns "" if nothing can be found.
1657#
1658
1659proc _tkiFindRef { w labelstart xref } {
1660    upvar #0 $w wvars
1661    if {$xref == 1} {
1662        set nodeIdx 1
1663        set labelIdx 4
1664        set indexIdx 0
1665        set listvar "xrefinfo"
1666        set type "xref"
1667    } else {
1668        set nodeIdx 2
1669        set labelIdx 5
1670        set indexIdx 1
1671        set listvar "menuinfo"
1672        set type "menu"
1673    }
1674    set labelstart [string tolower $labelstart]
1675    set found ""
1676    foreach mi $wvars($listvar) {
1677        set label [lindex $mi $labelIdx]
1678        set label [string tolower $label]
1679        if { [string first $labelstart $label] == 0 } {
1680            lappend found $mi
1681        }
1682    }
1683    if { $found != "" } {
1684        set tw $w.main.text
1685        set geom [_tkiWinVisibleInfo $tw]
1686        set top [lindex $geom 0]
1687        set bottom [lindex $geom 1]
1688        foreach mi $found {
1689            # Now determine whether this element is currently visible.
1690            if {$xref == 0} {
1691                set currentindex [$w.main.text index "menu.first + [lindex $mi 0] lines - 2 lines"]
1692            } else {
1693                set currentindex [$w.main.text index "1.0 + [lindex $mi 2] c"]
1694            }
1695            if { [$tw compare $currentindex > $bottom] } {
1696                break
1697            } else {
1698                if { [$tw compare $currentindex >= $top] } {
1699                    tkiSetCursor $w [list $type [lindex $mi $indexIdx]]
1700                    return [lindex $mi $nodeIdx]
1701                }
1702            }
1703        }
1704        # none is visible; return first one.
1705        set entry [lindex $found 0]
1706        tkiSetCursor $w [list $type [lindex $entry $indexIdx]]
1707        return [lindex $entry $nodeIdx]
1708    }
1709    return ""
1710}
1711
1712#
1713# Perform various actions on the info window.
1714# Note that if the action requires prompting (searching or goto-node)
1715# then we have to play with the focus.  This can badly interact with
1716# the focus games played when unmapping popup menus, so the "idle"
1717# option should be used when called from a menu.
1718# (I don't know what this is about --A.B.)
1719#
1720
1721proc _tkiWinAction { w args } {
1722    upvar #0 $w wvars
1723    global tki
1724
1725    set arg0 [lindex $args 0]
1726    set arg1 [lindex $args 1]
1727
1728    _tkiWinPromptUnmap $w
1729    tkiStatusUpdate $w
1730    if {$arg0 != "scroll" && $arg0 != "nextlink"} {
1731        tkiScrollUpdate $w
1732    }
1733
1734    set toNode ""
1735    set toFile $wvars(fileKey)
1736    set toWindow $w
1737
1738    case $arg0 {
1739        idle {
1740            after 1 _tkiWinAction $w [lrange $args 1 end]
1741            return
1742        }
1743        quit {
1744            catch {unset wvars}
1745            catch {destroy $w}
1746            # XXX: !!This is a major hack!!
1747            global tkiEmbed
1748            if { ![info exist tkiEmbed] && [winfo children .] == "" } {
1749                destroy .
1750            }
1751            return
1752        }
1753        goto {
1754            _tkiWinPromptMap $w goto "Go to (FILE) or NODE:" $arg1
1755            return
1756        }
1757        tclcmd {
1758            _tkiWinPromptMap $w tclcmd "Tcl cmd:" $arg1
1759            return
1760        }
1761        search {
1762            case $arg1 {
1763                "forwIncr" {
1764                    if {$wvars(inSearch) == 0} {
1765                        tkiStatus "No search to continue. Hit `s' to start one." $w 0
1766                    } else {
1767                        set tki(curWindow) $w
1768                        if {[searchboxNext searchkey $w]==-1} {
1769                            _tkiSearchFileForw $w $wvars(searchStr) $wvars(searchRegexpB) $wvars(searchCaseB) 1
1770                        }
1771                    }
1772                }
1773                "backIncr" {
1774                    if {$wvars(inSearch) == 0} {
1775                        tkiStatus "No search to continue. Hit `r' to start one." $w 0
1776                    } else {
1777                        set tki(curWindow) $w
1778                        if {[searchboxPrev searchkey $w]==-1} {
1779                            _tkiSearchFileBackw $w $wvars(searchStr) $wvars(searchRegexpB) $wvars(searchCaseB) 1
1780                        }
1781                    }
1782                }
1783                "forwRegexp" {
1784                    set wvars(searchRegexpB) 1
1785                    set wvars(searchBackB) 0
1786                    _tkiWinPromptMap $w search "Search:"
1787                }
1788                "forwExact" {
1789                    set wvars(searchRegexpB) 0
1790                    set wvars(searchBackB) 0
1791                    _tkiWinPromptMap $w search "Search:"
1792                }
1793                "backExact" {
1794                    set wvars(searchRegexpB) 0
1795                    set wvars(searchBackB) 1
1796                    _tkiWinPromptMap $w search "Search:"
1797                }
1798                "backRegexp" {
1799                    set wvars(searchRegexpB) 1
1800                    set wvars(searchBackB) 1
1801                    _tkiWinPromptMap $w search "Search:"
1802                }
1803            }
1804            return
1805        }
1806        indexlookup {
1807            _tkiWinPromptMap $w indexlookup "Index lookup (RET jumps to Index):"
1808            return
1809        }
1810        indexnext {
1811            set infoFileKey $tki(infoFileKey-$wvars(fileKey))
1812            if { $wvars(indexInfoFileKey) != $infoFileKey } {
1813                tkiStatus "No index lookup to continue. Hit `i' to start one." $w 0
1814                return
1815            }
1816            set number [expr [llength $wvars(indexEntries)] - $wvars(indexEntriesIndex) - 1]
1817            if { $number < 0 } {
1818                tkiStatus "No more index matches. Hit `i' to start new lookup." $w 0
1819            } else {
1820                set toNode [lindex [lindex $wvars(indexEntries) $wvars(indexEntriesIndex)] 0]
1821                if {[lindex [tkiWinShow $toNode $wvars(fileKey) $w] 0]!=""} {
1822                    TextSearch $w.main.text $wvars(indexString) searchkey 0
1823                    searchboxNext searchkey $w 0.0
1824                    if { $number == 1 } {
1825                        tkiStatus "Found \"[lindex [lindex $wvars(indexEntries) $wvars(indexEntriesIndex)] 1]\". Hit `,' for 1 more index match." $w 1
1826                    } elseif { $number > 1 } {
1827                        tkiStatus "Found \"[lindex [lindex $wvars(indexEntries) $wvars(indexEntriesIndex)] 1]\". Hit `,' for $number more index matches." $w 1
1828                    } else {
1829                        tkiStatus "Found \"[lindex [lindex $wvars(indexEntries) $wvars(indexEntriesIndex)] 1]\". No more index matches." $w 1
1830                    }
1831                }
1832                incr wvars(indexEntriesIndex)
1833            }
1834            return
1835        }
1836        manual {
1837            set tki(curWindow) $w
1838            if {[catch {selection get} manpage] || $manpage == "" } {
1839                set wvars(manB) [expr { $arg1 != "apropos"}]
1840                _tkiWinPromptMap $w manual "Unix manual page:"
1841            } else {
1842                $w conf -cursor $tki(waitCursor)
1843                $w.main.text conf -cursor $tki(waitCursor)
1844                _tkiWinManPage $w $manpage [expr { $arg1 == "apropos"}]
1845                $w conf -cursor  $tki(normCursor)
1846                $w.main.text conf -cursor $tki(normCursor)
1847            }
1848            return
1849        }
1850        last {
1851            set idx [expr { [llength $wvars(lastNodes)] - 1 } ]
1852            if { $idx >= 0 } {
1853                set lastinfo [lindex $wvars(lastNodes) $idx]
1854                set toFile [lindex $lastinfo 0]
1855                set toNode [lindex $lastinfo 1]
1856                set topline [lindex $lastinfo 2]
1857                set cursorInfo [lindex $lastinfo 3]
1858                set dummy $wvars(lastNodes)
1859                set wvars(lastNodes) [lreplace $wvars(lastNodes) $idx end]
1860                if { $arg1 == "redirect" } {
1861                    set wvars(noLastInfoUpdate) 1
1862                    _tkiWinAction $w redirect [list $toNode $toFile]
1863                    set wvars(lastNodes) $dummy
1864                    return
1865                }
1866                if { $arg1 == "other" } {
1867                    set wvars(noLastInfoUpdate) 1
1868                    _tkiWinAction $w newwin [list $toNode $toFile]
1869                    set wvars(lastNodes) $dummy
1870                    return
1871                }
1872                set wvars(noLastInfoUpdate) 1
1873                _tkiJumpTo $w $toNode $toFile $topline $cursorInfo
1874                return
1875            } else {
1876                tkiStatus "Can't go back any further." $w 0
1877                return
1878            }
1879        }
1880        up {
1881            set toNode [lindex $wvars(nodeinfo) 3]
1882            if { $toNode == "" } {
1883                tkiStatus "This node does not specify an \"up\" node." $w 0
1884                return
1885            }
1886            if { $arg1 == "other" } {
1887                set toWindow ""
1888            } else {
1889                if { $arg1 == "redirect" } {
1890                    _tkiWinAction $w redirect [list $toNode $toFile]
1891                    return
1892                }
1893            }
1894        }
1895        prev {
1896            set toNode [lindex $wvars(nodeinfo) 4]
1897            if { $toNode == "" } {
1898                tkiStatus "No previous section. Hit `\[' for predecessor node." $w 0
1899                return
1900            }
1901            if { $arg1 == "other" } {
1902                set toWindow ""
1903            } else {
1904                if { $arg1 == "redirect" } {
1905                    _tkiWinAction $w redirect [list $toNode $toFile]
1906                    return
1907                }
1908            }
1909        }
1910        next {
1911            set toNode [lindex $wvars(nodeinfo) 5]
1912            if { $toNode == "" } {
1913                tkiStatus "No next section. Hit `]' for successor node." $w 0
1914                return
1915            }
1916            if { $arg1 == "other" } {
1917                set toWindow ""
1918            } else {
1919                if { $arg1 == "redirect" } {
1920                    _tkiWinAction $w redirect [list $toNode $toFile]
1921                    return
1922                }
1923            }
1924        }
1925        dir  {
1926            if { $arg1 =="" } {
1927                if { [lindex $wvars(nodeinfo) 2] != "dir" } {
1928                    set toNode "(dir)"
1929                } else {
1930                    tkiStatus "This is already the dir info file." $w 0
1931                    return
1932                }
1933            } else {
1934                set toNode "($arg1/dir)"
1935            }
1936        }
1937        otherdir {
1938            set toNode "(dir)"
1939            if { $arg1 == "redirect" } {
1940                _tkiWinAction $w redirect [list $toNode $toFile]
1941                return
1942            } else {
1943                set toWindow ""
1944            }
1945        }
1946        top {
1947            if { $arg1 == "other" } {
1948                set toWindow ""
1949                set toNode "Top"
1950            } else {
1951                if { $arg1 == "redirect" } {
1952                    _tkiWinAction $w redirect [list Top $toFile]
1953                    return
1954                } elseif { [lindex $wvars(nodeinfo) 1] != "Top" } {
1955                    set toNode "Top"
1956                } else {
1957                    tkiStatus "This is already the top node." $w 0
1958                    return
1959                }
1960            }
1961        }
1962        nextlink {
1963            if { [tkiNextLink $w $arg1] == ""} {
1964                _tkiWinAction $w scroll $arg1
1965                tkiNextLink $w $arg1
1966            } else {
1967                tkiScrollUpdate $w
1968            }
1969            return
1970        }
1971        followlink {
1972            set toNode [tkiCursorLink $w]
1973            if { $toNode == ""} {
1974                return
1975            }
1976            if { $arg1 == "new"} {
1977                set toWindow ""
1978            }
1979        }
1980        othermenu {
1981            if { [info exist wvars(menuinfo)] } {
1982                set menuitem [lindex $wvars(menuinfo) $arg1]
1983                set toNode [lindex $menuitem 2]
1984                if { $toNode != "" } {
1985                    tkiSetCursor $w [list menu $arg1]
1986                    _tkiWinAction $w newwin [list $toNode $toFile]
1987                    return
1988                }
1989            }
1990        }
1991        successor {
1992            if { $arg1 == "forw" } {
1993                set toNode [_tkiLogicalNext $w]
1994                if { $toNode == "" } {
1995                    tkiStatus "No logical successor node." $w 0
1996                    return
1997                }
1998            } else {
1999                set toNode [_tkiLogicalPrev $w]
2000                if { $toNode == "" } {
2001                    tkiStatus "No logical predecessor node." $w 0
2002                    return
2003                }
2004            }
2005        }
2006        newwin {
2007            set tw $w.main.text
2008            $tw conf -cursor $tki(waitCursor)
2009            $w conf -cursor $tki(waitCursor)
2010            # Information to be passed to the new window:
2011            _tkiWinGetGeom $tw
2012            set tki(lastDir) $wvars(lastDir)
2013            set tki(promptHistory) $wvars(promptHistory)
2014            if { $arg1 ==""} {
2015                set tki(lastNodes) $wvars(lastNodes)
2016                set tki(history) $wvars(history)
2017                tkiWinShow [lindex $wvars(nodeinfo) 1] $wvars(fileKey)
2018            } else {
2019                set tki(lastNodes) [_tkiLastInfo $w "" ""]
2020                set tki(history) [_tkiWinHistoryAdd $w "" "" 1]
2021                eval tkiWinShow $arg1
2022            }
2023            $tw conf -cursor $tki(normCursor)
2024            $w conf -cursor $tki(normCursor)
2025            return
2026        }
2027        redirect {
2028            set tw $w.main.text
2029            $tw conf -cursor $tki(waitCursor)
2030            $w conf -cursor $tki(waitCursor)
2031            if { $wvars(redirectWindow) == "" || ![winfo exist $wvars(redirectWindow)] } {
2032                # Information to be passed to the new window:
2033                _tkiWinGetGeom $tw
2034                set tki(lastDir) $wvars(lastDir)
2035                set tki(lastNodes) [_tkiLastInfo $w "" ""]
2036                set tki(history) [_tkiWinHistoryAdd $w "" "" 1]
2037                set tki(promptHistory) $wvars(promptHistory)
2038                set wvars(redirectWindow) ""
2039            } else {
2040                # This is necessary if redirect was called from last...
2041                set wvars(noLastInfoUpdate) 0
2042
2043                set tki(lastNodes) ""
2044                set tki(history) ""
2045                set tki(promptHistory) ""
2046                set tki(lastDir) $wvars(lastDir)
2047            }
2048            if { $arg1 == ""} {
2049                set result [tkiWinShow [lindex $wvars(nodeinfo) 1] $wvars(fileKey) $wvars(redirectWindow)]
2050            } else {
2051                set result [eval tkiWinShow $arg1 $wvars(redirectWindow)]
2052            }
2053            set wvars(redirectWindow) [lindex $result 1]
2054            $tw conf -cursor $tki(normCursor)
2055            $w conf -cursor $tki(normCursor)
2056            return
2057        }
2058        transientmenu {
2059            $w.transientmenu post [expr [winfo pointerx $w] +4] [winfo pointery $w]
2060            grab $w.transientmenu
2061            return
2062        }
2063        menu {
2064            if { [info exist wvars(menuinfo)] } {
2065                if { $arg1 =="" } {
2066                    set tki(curWindow) $w
2067                    _tkiWinPromptMap $w menu "Beginning of Menu entry:"
2068                    return
2069                } else {
2070                    set menuitem [lindex $wvars(menuinfo) $arg1]
2071                    if { $menuitem == "" } {
2072                        tkiStatus "No such menu entry." $w 0
2073                        return
2074                    } else {
2075                        set toNode [lindex $menuitem 2]
2076                        tkiSetCursor $w [list menu $arg1]
2077                    }
2078                }
2079            } else {
2080                tkiStatus "No menu in this node." $w 0
2081                return
2082            }
2083        }
2084        xref {
2085            if { $wvars(xrefinfo) != "" } {
2086                set tki(curWindow) $w
2087                _tkiWinPromptMap $w xref  "Beginning of Xref label:"
2088                return
2089            } else {
2090                tkiStatus "No crossreferences in this node." $w 0
2091                return
2092            }
2093        }
2094        scroll {
2095            if { $wvars(scrollForwardHitBottom) == 1 && $arg1 != "forw" } {
2096                set wvars(scrollForwardHitBottom) 0
2097            }
2098            if { $wvars(scrollBackwardHitTop) == 1 && $arg1 != "back" } {
2099                set wvars(scrollBackwardHitTop) 0
2100            }
2101            case $arg1 {
2102                forw     { _tkiScroll $w forw; return }
2103                back     { _tkiScroll $w back; return }
2104                top      { $w.main.text yview moveto 0; return }
2105                bottom   { $w.main.text yview moveto 1; return }
2106                lineup   { $w.main.text yview scroll 1 units; return }
2107                linedown { $w.main.text yview scroll -1 units; return }
2108                menu     {
2109                    if [info exist wvars(menuinfo)] {
2110                        $w.main.text yview [$w.main.text index "menu.first - 1 lines"]
2111                        return
2112                    }
2113                }
2114            }
2115        }
2116    }
2117    if { $toNode == "" } {
2118        tkiBell
2119    } else {
2120        if { $toWindow == "" } {
2121            _tkiWinAction $w newwin [list $toNode $toFile]
2122        } else {
2123            tkiWinShow $toNode $toFile $toWindow
2124        }
2125    }
2126}
2127
2128proc tkiBell {} {
2129    bell
2130}
2131
2132proc tkiInterrupt {} {
2133    global tki
2134    set tki(interrupt) 1
2135    tkiBell
2136}
2137
2138#
2139# Scroll one page down resp. up. If already at end, determine the
2140# logical successor of the current page and jump there.
2141#
2142
2143proc _tkiScroll { w direction } {
2144    global tki; upvar #0 $w wvars
2145
2146    set tki(curWindow) $w
2147    if { $tki(scrollThroughB) } {
2148        if { $direction == "forw"} {
2149            if { $wvars(scrollForwardHitBottom) } {
2150                _tkiWinAction $w successor forw
2151            } else {
2152                if { [$w.main.text dlineinfo "end - 1 lines"] == "" } {
2153                    _tkiInsertMarkScroll $w 1
2154                }
2155                if { [$w.main.text dlineinfo "end - 1 lines"] != "" } {
2156                    tkiStatus "At end. Hit key again for successor node." $w 0
2157                    set wvars(scrollForwardHitBottom) 1
2158                }
2159            }
2160        } else {
2161            if { $wvars(scrollBackwardHitTop) } {
2162                _tkiWinAction $w successor back
2163            } else {
2164                if { [$w.main.text dlineinfo "0.1"] == "" } {
2165                    _tkiInsertMarkScroll $w -1
2166                }
2167                if { [$w.main.text dlineinfo "0.1"] != "" } {
2168                    tkiStatus "At beginning. Hit key again for predecessor node." $w 0
2169                    set wvars(scrollBackwardHitTop) 1
2170                }
2171            }
2172        }
2173    } else {
2174        if { $direction == "forw"} {
2175            if { [$w.main.text dlineinfo "end - 1 lines"] == "" } {
2176                _tkiInsertMarkScroll $w 1
2177            }
2178            if { [$w.main.text dlineinfo "end - 1 lines"] != "" } {
2179                tkiStatus "At end. Hit `]' for successor node." $w 0
2180            }
2181        } else {
2182            if { [$w.main.text dlineinfo "0.1"] == "" } {
2183                _tkiInsertMarkScroll $w -1
2184            }
2185            if { [$w.main.text dlineinfo "0.1"] != "" } {
2186                tkiStatus "At beginning. Hit `\[' for predecessor node." $w 0
2187            }
2188        }
2189    }
2190}
2191
2192#
2193# Scroll the textwindow $w dir pages, inserting the page separator
2194# correctly.
2195#
2196proc _tkiInsertMarkScroll {w dir} {
2197    global tki
2198    if {$tki(pageSepB)} {
2199        upvar #0 $w wvars
2200        set tw $w.main.text
2201        set geom [_tkiWinVisibleInfo $tw]
2202        set topleft [lindex $geom 0]
2203        set bottomright [lindex $geom 1]
2204        set columns [lindex $geom 4]
2205        set insertString ""
2206        for {set idx 1} {$idx <= $columns} {incr idx} {
2207            set insertString "${insertString}_"
2208        }
2209        set ranges [$tw tag ranges separator]
2210        $tw conf -state normal
2211        if {$dir == "1"} {
2212            $tw mark set insertPos  "$bottomright + 1 c"
2213            if {$ranges != ""} {
2214                eval $tw delete $ranges
2215                $tw yview scroll -1 units
2216            }
2217            $tw yview scroll 1 pages
2218            $tw insert insertPos "${insertString}\n" separator
2219        } else {
2220            $tw mark set insertPos  "$topleft"
2221            if {$ranges != ""} {
2222                eval $tw delete $ranges
2223                $tw yview scroll 1 units
2224            }
2225            $tw yview scroll -1 pages
2226            $tw insert insertPos "${insertString}\n" separator
2227        }
2228        $tw conf -state disabled
2229    } else {
2230        $w.main.text yview scroll $dir pages;
2231    }
2232}
2233
2234#
2235# Highlight the next link. Return "" if no next link on the current page.
2236# In that case, if the cursor text is not currently visible on the screen,
2237# remove it everywhere.
2238#
2239proc tkiNextLink { w direction } {
2240    set tw $w.main.text
2241    set geom [_tkiWinVisibleInfo $tw]
2242    set top [lindex $geom 0]
2243    set bottom [lindex $geom 1]
2244
2245    set cursorranges [$tw tag ranges cursor]
2246    if { $cursorranges == ""} {
2247        set cursorStart "end"
2248        set cursorEnd "1.0"
2249    } else {
2250        set cursorStart [lindex $cursorranges 0]
2251        set cursorEnd [lindex $cursorranges 1]
2252    }
2253    if { $direction == "forw" } {
2254        if { [$tw compare $top < $cursorEnd] } {
2255            if { [$tw compare $bottom >= $cursorEnd] } {
2256                set start $cursorEnd
2257            } else {
2258                set start $top
2259            }
2260        } else {
2261            set start $top
2262        }
2263
2264        set menu [$tw tag nextrange menukey $start $bottom]
2265        set cross [$tw tag nextrange xrefkey $start $bottom]
2266        if { $menu == "" } {
2267            set link $cross
2268        } elseif { $cross == "" } {
2269            set link $menu
2270        } elseif { [lindex $cross 0] < [lindex $menu 0] } {
2271            set link $cross
2272        } else {
2273            set link $menu
2274        }
2275    } else {
2276        if { [$tw compare $top <= $cursorStart] } {
2277            if { [$tw compare $bottom > $cursorStart] } {
2278                set start $cursorStart
2279            } else {
2280                set start $bottom
2281            }
2282        } else {
2283            set start $bottom
2284        }
2285
2286        set menu [_tkiprevrange $tw menukey $start $top]
2287        set xref [_tkiprevrange $tw xrefkey $start $top]
2288        if { $menu == "" } {
2289            set link $xref
2290        } elseif { $xref == "" } {
2291            set link $menu
2292        } elseif { [lindex $xref 0] > [lindex $menu 0] } {
2293            set link $xref
2294        } else {
2295            set link $menu
2296        }
2297    }
2298    if { $link == "" } {
2299        if { [$tw compare $top > $cursorEnd] || [$tw compare $bottom <= $cursorStart] } {
2300            $tw tag remove cursor $cursorStart $cursorEnd
2301        }
2302        return ""
2303    }
2304    $tw tag remove cursor $cursorStart $cursorEnd
2305    $tw tag add cursor [lindex $link 0] [lindex $link 1]
2306    $tw tag raise cursor
2307    return 1
2308}
2309
2310
2311# Return the info node corresponding to the highlighted link
2312proc tkiCursorLink { w } {
2313    upvar #0 $w wvars
2314
2315    set tw $w.main.text
2316    set cursorranges [$tw tag ranges cursor]
2317    if { $cursorranges == ""} {
2318        return ""
2319    } else {
2320        set cursorStart [lindex $cursorranges 0]
2321    }
2322    set taglist [$tw tag names $cursorStart]
2323    set length [llength $taglist]
2324    set tagindex ""
2325    foreach tag $taglist {
2326        if [regexp {^menu([0-9]+)} $tag dummy tagindex] {
2327            set wvars(cursorInfo) [list "menu" $tagindex]
2328            return [lindex [lindex $wvars(menuinfo) $tagindex] 2]
2329        } elseif [regexp {^xref([0-9]+)} $tag dummy tagindex] {
2330            set wvars(cursorInfo) [list "xref" $tagindex]
2331            return [lindex [lindex $wvars(xrefinfo) $tagindex] 1]
2332        }
2333    }
2334    return ""
2335}
2336proc tkiHighlightCursor { w cursorInfo } {
2337    upvar #0 $w wvars
2338
2339    if { $cursorInfo == "" } {
2340        return
2341    }
2342
2343    set tw $w.main.text
2344    set type [lindex $cursorInfo 0]
2345    set index [lindex $cursorInfo 1]
2346    set ranges [$tw tag ranges "${type}key"]
2347    set start [lindex $ranges [expr 2 * $index ]]
2348    set end [lindex $ranges [expr 1+ 2 * $index ]]
2349    set cursorranges [$tw tag ranges cursor]
2350    if { $cursorranges != "" } {
2351        eval $tw tag remove cursor $cursorranges
2352    }
2353    $tw tag add cursor $start $end
2354    $tw tag raise cursor
2355}
2356proc tkiSetCursor { w cursorInfo } {
2357    upvar #0 $w wvars
2358
2359    tkiHighlightCursor $w $cursorInfo
2360    set wvars(cursorInfo) $cursorInfo
2361}
2362
2363# Find the logical successor of the node displayed in window w.
2364
2365proc _tkiLogicalNext { w } {
2366    global tki; upvar #0 $w wvars
2367
2368    if { [info exist wvars(menuinfo)]
2369         && ![string match "*Index" [lindex $wvars(nodeinfo) 1]] } {
2370        return [lindex [lindex $wvars(menuinfo) 0] 2 ]
2371    } else {
2372        set next [lindex $wvars(nodeinfo) 5]
2373        set up [lindex $wvars(nodeinfo) 3]
2374        if { $next != "" && $next != $up } {
2375            return $next
2376        } else {
2377            while { $up != "" } {
2378                set upNodeRef [tkiGetNodeRef $up $wvars(fileKey) "" $wvars(lastDir)]
2379                set upNodeInfo [lindex $tki(nodesinfo-[lindex $upNodeRef 1]) [lindex $upNodeRef 0]]
2380                set upNext [lindex $upNodeInfo 5]
2381                if { $upNext != ""} {
2382                    return $upNext
2383                } else {
2384                    set up [lindex $upNodeInfo 3]
2385                }
2386            }
2387            return ""
2388        }
2389    }
2390}
2391
2392
2393# Find the logical predecessor of the node displayed in window w.
2394proc _tkiLogicalPrev { w } {
2395    global tki; upvar #0 $w wvars
2396
2397    set prev [lindex $wvars(nodeinfo) 4]
2398    set up [lindex $wvars(nodeinfo) 3]
2399    if { $prev == "" && $up == "" } {
2400        return ""
2401    }
2402    if { $prev == "" || $prev == $up } {
2403        return $up
2404    }
2405    set node $prev
2406    set fileKey $wvars(fileKey)
2407    while 1 {
2408        set nodeRef [tkiGetNodeRef $node $fileKey "" $wvars(lastDir)]
2409        set nodeIdx [lindex $nodeRef 0]
2410        set fileKey [lindex $nodeRef 1]
2411        if { ![info exist tki(menuinfo-$fileKey-$nodeIdx) ] } {
2412            return $node
2413        }
2414        set nodeMenu $tki(menuinfo-$fileKey-$nodeIdx)
2415        set lastEntry [lindex $nodeMenu end]
2416        set node [lindex $lastEntry 2]
2417    }
2418}
2419
2420#
2421# Utility function for turning the "-acc" options from
2422# menus into actual bindings.
2423# Traverse {menu}, and install accelerators onto {winSpec}.
2424# {winSpec} may be a list of windows.  {menu} may be a menu, a
2425# menu button, or a frame containing menu buttons.
2426# Accelerator sequences may be any sequence of "normal" characters,
2427# or a normal char prefixed by "^" for Control.
2428# This code is cut&pasted from "tkgraph/lib/topwin.tcl topWin.BindAccels()".
2429#
2430
2431proc _tkiBindAccels { winSpec menu } {
2432    switch [winfo class $menu] {
2433      Frame {
2434	foreach submenu [winfo children $menu] {
2435	    _tkiBindAccels $winSpec $submenu
2436	}
2437      }
2438      Menubutton {
2439	_tkiBindAccels $winSpec [lindex [$menu conf -menu] 4]
2440      }
2441      Menu {
2442	set lastIdx [$menu index last]
2443        if { $lastIdx == "none" } { return }
2444	for {set idx 0} {$idx <= $lastIdx} {incr idx} {
2445	    if [catch {$menu entryconf $idx -acc} acc] continue
2446	    set acc [lindex $acc 4]
2447	    if { $acc != "" && $acc != "==>" } {
2448		regsub -all "\\^(.)" $acc "<Control-\\1>" acc
2449		regsub -all "<(.)>" $acc "<Key-\\1>" acc
2450		foreach win $winSpec {
2451                   bind $win $acc "[$menu entrycget $idx -command] ;break"
2452		}
2453	    }
2454	    if { ! [catch {$menu entryconf $idx -menu} submenu] } {
2455		set submenu [lindex $submenu 4]
2456		if { $submenu != "" } {
2457		    _tkiBindAccels $winSpec $submenu
2458		}
2459	    }
2460	}
2461      }
2462    }
2463}
2464proc _tkiWinBind { w } {
2465    global tki tkiEmbed;
2466
2467    set tw $w.main.text
2468    _tkiBindAccels "$w.main.text" $w.bar
2469    foreach win "$w.main.text" {
2470        # Caution: Don't bind the keysyms SunPageDown and SunFind: it will
2471        # break on Win95.
2472        bind $win <Key-Help>    {tkiWinShow {(builtin)Quick Help} {} {Docs}
2473                                 break}
2474        bind $win <Key-F1>      {tkiWinShow {(builtin)Quick Help} {} {Docs}
2475                                 break}
2476	bind $win <Key-1>	"_tkiWinAction $w menu 0"
2477	bind $win <Key-2>	"_tkiWinAction $w menu 1"
2478	bind $win <Key-3>	"_tkiWinAction $w menu 2"
2479	bind $win <Key-4>	"_tkiWinAction $w menu 3"
2480	bind $win <Key-5>	"_tkiWinAction $w menu 4"
2481	bind $win <Key-6>	"_tkiWinAction $w menu 5"
2482	bind $win <Key-7>	"_tkiWinAction $w menu 6"
2483	bind $win <Key-8>	"_tkiWinAction $w menu 7"
2484	bind $win <Key-9>	"_tkiWinAction $w menu 8"
2485	bind $win <Control-Key-1>	"_tkiWinAction $w othermenu 0;break"
2486	bind $win <Control-Key-2>	"_tkiWinAction $w othermenu 1;break"
2487	bind $win <Control-Key-3>	"_tkiWinAction $w othermenu 2;break"
2488	bind $win <Control-Key-4>	"_tkiWinAction $w othermenu 3;break"
2489	bind $win <Control-Key-5>	"_tkiWinAction $w othermenu 4;break"
2490	bind $win <Control-Key-6>	"_tkiWinAction $w othermenu 5;break"
2491	bind $win <Control-Key-7>	"_tkiWinAction $w othermenu 6;break"
2492	bind $win <Control-Key-8>	"_tkiWinAction $w othermenu 7;break"
2493	bind $win <Control-Key-9>	"_tkiWinAction $w othermenu 8;break"
2494	bind $win <Key-space>		"_tkiWinAction $w scroll forw"
2495	bind $win <Control-Key-f>	"_tkiWinAction $w scroll forw"
2496	bind $win \}                   "_tkiWinAction $w scroll forw"
2497	bind $win <Control-Key-d>	"return"
2498	bind $win <Control-Key-v>	"_tkiWinAction $w scroll forw
2499                                         break"
2500        bind $win <Key-Next>            "_tkiWinAction $w scroll forw
2501                                         break"
2502        # PgDn on Sun Keypads:
2503	bind $win <Key-F35>		"_tkiWinAction $w scroll forw"
2504	bind $win <Key-Delete>		"_tkiWinAction $w scroll back
2505                                         break"
2506        bind $win <Key-Prior>           "_tkiWinAction $w scroll back
2507                                         break"
2508	bind $win <Key-BackSpace>	"_tkiWinAction $w scroll back
2509                                         break"
2510	bind $win <Control-Key-b>	"_tkiWinAction $w scroll back"
2511	bind $win <Alt-Key-v>		"_tkiWinAction $w scroll back"
2512	bind $win \{			"_tkiWinAction $w scroll back"
2513	bind $win <Meta-Key-v>		"_tkiWinAction $w scroll back"
2514        bind $win "<Key-Escape> v"      "_tkiWinAction $w scroll back"
2515        # PgUp on Sun Keypads:
2516	bind $win <Key-F29>		"_tkiWinAction $w scroll back"
2517        bind $win <Key-less>            "_tkiWinAction $w scroll top"
2518        bind $win <Key-b>               "_tkiWinAction $w scroll top"
2519        bind $win <Key-Home>            "_tkiWinAction $w scroll top
2520                                         break"
2521        # Home on Sun Keypads:
2522        bind $win <Key-F27>             "_tkiWinAction $w scroll top
2523                                         break"
2524        bind $win <Key-End>             "_tkiWinAction $w scroll bottom
2525                                         break"
2526        # End on Sun Keypads:
2527        bind $win <Key-F33>             "_tkiWinAction $w scroll bottom
2528                                         break"
2529        bind $win <Key-greater>         "_tkiWinAction $w scroll bottom"
2530        bind $win <Key-G>               "_tkiWinAction $w scroll bottom"
2531        bind $win <Key-e>               "_tkiWinAction $w scroll bottom"
2532        bind $win <Control-Key-m>       "_tkiWinAction $w scroll menu"
2533        bind $win <Key-j>               "_tkiWinAction $w scroll lineup"
2534        bind $win <Key-Down>            "_tkiWinAction $w scroll lineup
2535                                         break"
2536        bind $win <Key-Right>           "_tkiWinAction $w scroll lineup
2537                                         break"
2538        bind $win <Control-Key-n>       "_tkiWinAction $w scroll lineup
2539                                         break"
2540        bind $win <Key-k>               "_tkiWinAction $w scroll linedown"
2541        bind $win <Control-Key-p>       "_tkiWinAction $w scroll linedown
2542                                         break"
2543        bind $win <Key-Up>              "_tkiWinAction $w scroll linedown
2544                                         break"
2545        bind $win <Key-Left> 	        "_tkiWinAction $w scroll linedown
2546                                         break"
2547        bind $win <Alt-Key-Left>        "_tkiWinAction $w last; break"
2548        bind $win <Meta-Key-Left>       "_tkiWinAction $w last; break"
2549        bind $win <Key-C>               "_tkiWinAction $w quit; break"
2550        if { ![info exists tkiEmbed] } {
2551            bind $win <Key-Q>               "exit"
2552        }
2553        bind $win <Key-Tab>             "_tkiWinAction $w nextlink forw;break"
2554		bind $win <Key-ISO_Left_Tab>    "_tkiWinAction $w nextlink back;break"
2555		bind $win <Control-Key-Tab>     "_tkiWinAction $w nextlink back;break"
2556        bind $win <Shift-Key-Tab>       "_tkiWinAction $w nextlink back;break"
2557        bind $win <Meta-Key-Tab>        "_tkiWinAction $w nextlink back;break"
2558        bind $win <Alt-Key-Tab>         "_tkiWinAction $w nextlink back;break"
2559	bind $win (		        "_tkiWinAction $w goto ("
2560        bind $win <Key-Return>          "_tkiWinAction $w followlink; break"
2561        bind $win <Key-KP_Enter>        "_tkiWinAction $w followlink; break"
2562        bind $win <Control-Key-Return>  "_tkiWinAction $w followlink new;break"
2563        bind $win <Control-Key-KP_Enter> "_tkiWinAction $w followlink new;break"
2564        bind $win <Control-Key-c>       "tkiInterrupt"
2565        bind $win <Control-Key-g>       "tkiInterrupt"
2566        bind $win <Button-3>  "_tkiButton3 $w; break"
2567        bind $win <Button-2> "_tkiButton2 $w"
2568        bind $win <ButtonRelease-2> "_tkiButtonRelease2main $w"
2569
2570        # This is really ugly but I don't know how else to prohibit
2571        # the key "Alt-f" (used to access the menu bar) from executing
2572        # the script associated with "f" -- A.B.
2573        bind $win <Control-Key-l>       "return"
2574        bind $win <Alt-F1>              "return"
2575        bind $win <Meta-F1>             "return"
2576        bind $win <Alt-f>               "return"
2577        bind $win <Meta-f>              "return"
2578        bind $win <Alt-d>               "return"
2579        bind $win <Meta-d>              "return"
2580        bind $win <Alt-n>               "return"
2581        bind $win <Meta-n>              "return"
2582        bind $win <Alt-s>               "return"
2583        bind $win <Meta-s>              "return"
2584        bind $win <Alt-o>               "return"
2585        bind $win <Meta-o>              "return"
2586        bind $win <Alt-h>               "return"
2587        bind $win <Meta-h>              "return"
2588        bind $win <Alt-p>               "return"
2589        bind $win <Meta-p>              "return"
2590    }
2591    focus $w.main.text
2592}
2593
2594#
2595# Functions to be bound to mouse events
2596#
2597
2598proc _tkiButton2 {w} {
2599    global tki; upvar #0 $w wvars
2600
2601    if { [$w.main.text cget -cursor] == $tki(normCursor) } {
2602        $w.main.text configure -cursor $tki(handCursor)
2603    }
2604    if { $wvars(scrollForwardHitBottom) } {
2605        set wvars(scrollForwardHitBottom) 0
2606    }
2607    if { $wvars(scrollBackwardHitTop) } {
2608        set wvars(scrollBackwardHitTop) 0
2609    }
2610}
2611
2612proc _tkiButton3 {w} {
2613    global tki
2614    if {$tki(breakBindings) == 0} {
2615        _tkiWinAction $w transientmenu
2616    }
2617    set tki(breakBindings) 0
2618}
2619
2620proc _tkiButtonRelease2main {w} {
2621    global tki
2622
2623    tkiStatusUpdate $w
2624    tkiScrollUpdate $w
2625    if { [$w.main.text cget -cursor] == $tki(handCursor) } {
2626        $w.main.text configure -cursor $tki(normCursor)
2627    }
2628}
2629
2630proc _tkiLeaveLink {tw} {
2631    global tki
2632    if { [$tw cget -cursor] == $tki(linkCursor) } {
2633        $tw configure -cursor $tki(normCursor)
2634    }
2635}
2636
2637proc _tkiButtonRelease2 {w y idx toNode fileKey type} {
2638    global tki
2639    if {abs($y - $tki(y)) < 7} {
2640        tkiSetCursor $w [list $type $idx]
2641        _tkiWinAction $w newwin [list $toNode $fileKey]
2642    }
2643}
2644
2645proc _tkiShiftButtonRelease1 {w idx toNode fileKey type} {
2646    tkiSetCursor $w [list $type $idx]
2647    _tkiWinAction $w newwin [list $toNode $fileKey]
2648}
2649
2650proc _tkiButtonRelease3 {w idx toNode fileKey type} {
2651    tkiSetCursor $w [list $type $idx]
2652    _tkiWinAction $w redirect [list $toNode $fileKey]
2653}
2654
2655proc _tkiButtonRelease1 {w x y idx toNode fileKey type} {
2656    global tki
2657    if {abs($x - $tki(x)) + abs($y - $tki(y)) < 8} {
2658        tkiSetCursor $w [list $type $idx]
2659        tkiWinShow $toNode $fileKey $w
2660    }
2661}
2662
2663# Bind to mouse events for the action buttons. This removes the use of
2664# and dependency on internal Tk procedure names like tkButtonDown
2665# (before tk8.4) or tk::ButtonDown (starting with tk8.4).
2666proc _tkiBindToButton {w b op {op2 ""} {op3 ""}} {
2667    if {$op3 == ""} {
2668      set op3 "$op redirect"
2669    }
2670    if {$op2 == ""} {
2671      set op2 "$op other"
2672    }
2673    set press [bind Button <Button-1>]
2674    set release [bind Button <ButtonRelease-1>]
2675
2676    bind $b <Button-1> "$press; break"
2677    bind $b <ButtonRelease-1> "$release; _tkiWinAction $w $op;break"
2678    bind $b <Button-2> "$press; break"
2679    bind $b <ButtonRelease-2> "$release; _tkiWinAction $w $op2; break"
2680    bind $b <Shift-Button-1> "$press; break"
2681    bind $b <Shift-ButtonRelease-1> "$release; _tkiWinAction $w $op2; break"
2682    bind $b <Control-Button-1> "$press; break"
2683    bind $b <Control-ButtonRelease-1> "$release; _tkiWinAction $w $op2; break"
2684    bind $b <Button-3> "$press; break"
2685    bind $b <ButtonRelease-3> "$release; _tkiWinAction $w $op3; break"
2686}
2687
2688
2689#
2690# Make a new toplevel info window (with class ``TkInfo''),
2691# filled with buttons and bindings.
2692#
2693# If the argument {w} is non-empty, it specifies either the path name
2694# of the info window to create (if {w} doesn't already exist),
2695# or the parent of the info window to create (if {w} does already exist).
2696# It is an error for both {w} and {w}'s parent to not exist.
2697# If {w} is empty, the info window will be created as a child of the
2698# root window.
2699#
2700# If given, {tag} is some text that will appear in the window title and
2701# icon title.
2702#
2703# The path name of the new info window will be returned.
2704#
2705
2706proc tkiWinCreate { {w ""} {tag ""} } {
2707    global tki balloonHelp tk_version tkiEmbed
2708
2709    if { $w == "" || [winfo exist $w] } {
2710	if { $w != "" && [winfo class $w] == "TkInfo" } {
2711	    # This check isn't strictly required, but it helps catch
2712	    # problems with Tk's multi-phase window destruction process.
2713	    error "Can't nest TkInfo windows."
2714	}
2715	set parent $w
2716	while 1 {
2717	    # I think (but I dont really remember) that I use [winfo parent]
2718	    # here instead of [winfo exist] b/c multi-phase destroy.
2719	    set w $parent.tki[tkiGetSN]
2720	    if { [catch {winfo parent $w}] } break
2721	}
2722    }
2723    lappend tki(windows) $w
2724    upvar #0 $w wvars
2725    set wvars(nodeinfo) ""
2726    set wvars(nodeSpec) ""
2727    set wvars(fileKey) ""
2728    set wvars(infonodename) "(builtin)Top"
2729    set wvars(lastDir) $tki(lastDir)
2730    set wvars(gotoStr) ""
2731    set wvars(promptmode) ""
2732    set wvars(searchStr) ""
2733    set wvars(statusPermanent) 0
2734    set wvars(oldStatus) ""
2735    set wvars(indexInfoFileKey) ""
2736    set wvars(noLastInfoUpdate) 0
2737    set wvars(redirectWindow) ""
2738    set wvars(searchOriginFileKey) ""
2739    set wvars(searchOriginNodeIdx) ""
2740    set wvars(promptHistory) $tki(promptHistory)
2741    set wvars(lastNodes) $tki(lastNodes)
2742    set wvars(history) $tki(history)
2743    set wvars(title) [expr {( $tag == "") ? "tkInfo" : "tkInfo:$tag"}]
2744
2745    toplevel $w -class TkInfo
2746    wm title $w $wvars(title)
2747    wm iconname $w $wvars(title)
2748    wm protocol $w WM_DELETE_WINDOW "_tkiWinAction $w quit"
2749    # iconbitmap only accepts xbm files, but xman.xpm is a pixmap.
2750    # wm iconbitmap $w "@xman.xpm"
2751
2752    set dd $w.bar; pack [frame $dd -borderwidth 2 -relief raised] \
2753      -side top -fill x
2754
2755    set ddm $dd.file.m
2756    pack [menubutton $dd.file -text "File" -und 0 -menu $ddm] -side left
2757    # tk4.0 doesn't know tear-off menus:
2758    if {$tk_version > 4.0} {
2759        if {$tk_version < 8} {
2760            menu $ddm -tearoffcommand "_tkiMenuTearOff $w"
2761        } else {
2762            menu $ddm
2763        }
2764    } else {
2765        menu $ddm -tearoff 0
2766    }
2767    $ddm add com -lab "Directory" -und 0 -acc d -command "_tkiWinAction $w dir"
2768    $ddm add com -lab "Go to File/Node... " -und 0 -acc g -command "_tkiWinAction $w goto"
2769    $ddm add com -lab "New Window " -und 0 -acc N -command "_tkiWinAction $w newwin"
2770    $ddm add com -lab "Man Page..." -und 0 -acc M -command "_tkiWinAction $w manual"
2771    $ddm add com -lab "Apropos..." -und 0 -acc A -command "_tkiWinAction $w manual apropos"
2772    $ddm add com -lab "Tcl Cmd..." -und 0 -acc ! -command "_tkiWinAction $w tclcmd"
2773    $ddm add sep
2774    $ddm add com -lab "Close Window" -und 0 -acc c -command "_tkiWinAction $w quit"
2775
2776    if { ![info exists tkiEmbed] } {
2777        $ddm add com -lab "Quit TkInfo" -und 0 -acc q -command "exit"
2778    }
2779
2780    if { [llength $tki(dirs)] > 1 } {
2781        set ddd $dd.dirs.m
2782        pack [menubutton $dd.dirs -text "Directories" -und 0 -menu $ddd] -side left
2783        menu $ddd
2784        foreach pp $tki(dirs) {
2785            $ddd add com -label "   $pp" \
2786                    -command [list _tkiWinAction $w dir $pp]
2787        }
2788    }
2789    set ddm $dd.node.m
2790    pack [menubutton $dd.node -text "Node" -und 0 -menu $ddm] -side left
2791    if {$tk_version > 4.0} {
2792        if {$tk_version < 8} {
2793            menu $ddm -tearoffcommand "_tkiMenuTearOff $w"
2794        } else {
2795            menu $ddm
2796        }
2797    } else {
2798        menu $ddm -tearoff 0
2799    }
2800    $ddm add com -lab "Next Section" -und 0 -acc n -command "_tkiWinAction $w next"
2801    $ddm add com -lab "Previous Section  " -und 0 -acc p -command "_tkiWinAction $w prev"
2802    $ddm add com -lab "Up" -und 0 -acc u -command "_tkiWinAction $w up"
2803    $ddm add com -lab "Back to Last" -und 8 -acc l -command "_tkiWinAction $w last"
2804    $ddm add com -lab "Successor" -und 0 -acc \] -command "_tkiWinAction $w successor forw"
2805    $ddm add com -lab "Predecessor" -und 1 -acc \[ -command "_tkiWinAction $w successor back"
2806    $ddm add com -lab "Top" -und 0 -acc t -command "_tkiWinAction $w top"
2807    $ddm add com -lab "Menu entry..." -und 0 -acc m -command "_tkiWinAction $w menu"
2808    $ddm add com -lab "Crossreference... " -und 7 -acc f -command "_tkiWinAction $w xref"
2809
2810
2811    set ddm $dd.search.m
2812    pack [menubutton $dd.search -text "Search" -und 0 -menu $ddm] -side left
2813    if {$tk_version > 4.0} {
2814        if {$tk_version < 8} {
2815            menu $ddm -tearoffcommand "_tkiMenuTearOff $w"
2816        } else {
2817            menu $ddm
2818        }
2819    } else {
2820        menu $ddm -tearoff 0
2821    }
2822    $ddm add com -lab "Index lookup (substring)... " -und 0 -acc i \
2823      -command "_tkiWinAction $w indexlookup"
2824    $ddm add com -lab "Continue index lookup" -acc , \
2825      -command "_tkiWinAction $w indexnext"
2826    $ddm add com -lab "Exact forward search... " -und 0 -acc s \
2827      -command "_tkiWinAction $w search forwExact"
2828    $ddm add com -lab "Regexp forward search... " -und 0 -acc / \
2829      -command "_tkiWinAction $w search forwRegexp"
2830    $ddm add com -lab "Continue forward search" -und 0 -acc ^s \
2831      -command "_tkiWinAction $w search forwIncr"
2832    $ddm add com -lab "Exact backward search... " -und 6 -acc r \
2833      -command "_tkiWinAction $w search backExact"
2834    $ddm add com -lab "Regexp backward search... " -und 8 -acc "\\" \
2835      -command "_tkiWinAction $w search backRegexp"
2836    $ddm add com -lab "Continue backward search" -acc ^r \
2837      -command "_tkiWinAction $w search backIncr"
2838
2839    set ddm $dd.history.m
2840    pack [menubutton $dd.history -text "History" -und 0 -menu $ddm] -side left
2841    if {$tk_version > 4.0} {
2842        if {$tk_version < 8} {
2843            menu $ddm -tearoffcommand "_tkiMenuTearOff $w"
2844        } else {
2845            menu $ddm
2846        }
2847    } else {
2848        menu $ddm -tearoff 0
2849    }
2850    set wvars(historyMenus) [list $ddm]
2851    _tkiCreateHistory $w $wvars(history)
2852
2853    set ddm $dd.options.m
2854    pack [menubutton $dd.options -text "Options" -und 0 -menu $ddm] -side left
2855    menu $ddm -disabledforeground [ $dd.search.m cget -foreground ]
2856    if {$tk_version > 4.0} {
2857        if {$tk_version < 8} {
2858            $ddm conf -tearoffcommand "_tkiMenuTearOff $w"
2859        }
2860    } else {
2861        $ddm conf -tearoff 0
2862    }
2863    $ddm add check -lab "Show info headers" -und 10 -var tki(rawHeadersB)
2864    $ddm add check -lab "Show buttons" -und 5 -var tki(showButtonsB)
2865    $ddm add check -lab "Balloon help" -und 4 -var tki(showBalloonsB)
2866    $ddm add check -lab "Scroll at bottom goes to successor" -und 4 -var tki(scrollThroughB)
2867    $ddm add check -lab "Scrolling inserts page separators" -und 5 -var tki(pageSepB)
2868    $ddm add check -lab "Show directory of node" -und 5 -var tki(showDirB)
2869    $ddm add check -lab "Time Status" -und 0 -var tki(timestatusB)
2870    $ddm add sep
2871    $ddm add com -lab "Link Highlighting:" -state disabled
2872    $ddm add radio -lab "Color" -und 0     -var tki(linklook) -val color
2873    $ddm add radio -lab "Font" -und 0      -var tki(linklook) -val font
2874    $ddm add radio -lab "Underline" -und 0 -var tki(linklook) -val underline
2875
2876    set ddm $dd.help.m
2877    # We use -after so that the Help menu doesn't disappear when window
2878    # is shrunk:
2879    pack [menubutton $dd.help -text "Help" -und 3 -menu $ddm] -side right -after $dd.file
2880    if {$tk_version > 4.0} {
2881        if {$tk_version < 8} {
2882            menu $ddm -tearoffcommand "_tkiMenuTearOff $w"
2883        } else {
2884            menu $ddm
2885        }
2886    } else {
2887        menu $ddm -tearoff 0
2888    }
2889    $ddm add com -lab "Quick Help" -und 0 -acc ? \
2890            -command [list tkiWinShow {(builtin)Quick Help} {} {Docs}]
2891    $ddm add com -lab "Documentation " -und 0 -acc h \
2892            -command [list tkiWinShow {(builtin)Top} {} {Docs}]
2893    $ddm add sep
2894    $ddm add com -lab "About tkInfo" -und 0 \
2895            -command [list tkiWinShow {(builtin)About} {} {Docs}]
2896
2897
2898    # We want to be able to access the menubar with Meta as well as with Alt:
2899    bind $w <Meta-Key> [bind all <Alt-Key>]
2900
2901    # The transient menu that appears when Button-3 is pressed:
2902    set wtm $w.transientmenu
2903    menu $wtm -tearoff 0
2904    # This appears to be necessary to circumvent a bug in Tk4.0.
2905    # Let's hope that it doesn't break anything else... (A.B.)
2906    if {$tk_version == 4.0} {
2907        global tkPriv
2908        set tkPriv(oldGrab) ""
2909    }
2910    bind $wtm <Unmap> "focus -force $w.main.text"
2911    bind $wtm <Button-1> "break"
2912    bind $wtm <Button-2> "break"
2913    $wtm add com -lab "Logical Successor" -acc \] -command "_tkiWinAction $w successor forw"
2914    # $wtm add com -lab "Logical Predecessor" -command "_tkiWinAction $w successor back"
2915    $wtm add com -lab "Back to Last Node  " -acc l -command "_tkiWinAction $w last"
2916    $wtm add com -lab "Next Section" -acc n -command "_tkiWinAction $w next"
2917    # $wtm add com -lab "Previous Section" -acc p -command "_tkiWinAction $w prev"
2918    $wtm add com -lab "Up" -acc u -command "_tkiWinAction $w up"
2919    $wtm add com -lab "Index Lookup" -acc i -command "_tkiWinAction $w indexlookup"
2920    $wtm add com -lab "New Window" -acc N -command "_tkiWinAction $w newwin"
2921
2922    set dd $w.main
2923    pack [frame $dd] -expand 1 -fill both
2924    pack [scrollbar $dd.vsb -orient vert -command "$dd.text yview"] \
2925            -side right -fill both
2926    pack [text $dd.text -state disabled -setgrid 1 -width 80 -wrap word] \
2927            -side left -expand 1 -fill both
2928    $dd.text conf -yscroll "$dd.vsb set"
2929    bind $dd.vsb <Any-Button> "tkiStatusUpdate $w; tkiScrollUpdate $w"
2930
2931    # We use "-after $w.bar" here so that the status line won't disappear
2932    # upon resizing of the window:
2933    set dd $w.s
2934    pack [frame $dd] -after $w.bar -side bottom -fill x
2935    pack [label $dd.filename -text "            " -rel sunken -padx 5 -pady 3] -side left
2936    pack [label $dd.status -anc w -rel sunken -padx 5 -pady 3 -width 8] \
2937            -side left -fill x -expand 1
2938    entry $dd.input -width 7 -rel sunken
2939    checkbutton $dd.regexp -width 8 -text "Regexp" -var ${w}(searchRegexpB)
2940    checkbutton $dd.case -width 8 -text "Case Sen" -var ${w}(searchCaseB)
2941    checkbutton $dd.back -width 8 -text "Backward" -var ${w}(searchBackB)
2942    radiobutton $dd.man -width 8 -text "Man page" -var ${w}(manB) -value 1
2943    radiobutton $dd.apropos -width 8 -text "Apropos" -var ${w}(manB) -value 0
2944    bind $dd.input <Return> "_tkiWinPromptOk $w"
2945    bind $dd.input <Escape> "_tkiWinPromptAbort $w"
2946    bind $dd.input <Any-Control-g> "_tkiWinPromptAbort $w"
2947    bind $dd.input <Control-u> "$dd.input delete 0 end"
2948    bind $dd.input <Key-Up> "_tkiWinPromptScroll $w up"
2949    bind $dd.input <Meta-Key-p> "_tkiWinPromptScroll $w up; break"
2950    bind $dd.input <Alt-Key-p> "_tkiWinPromptScroll $w up; break"
2951    bind $dd.input <Control-Key-p> "_tkiWinPromptScroll $w up; break"
2952    bind $dd.input <Key-Down> "_tkiWinPromptScroll $w down"
2953    bind $dd.input <Control-Key-n> "_tkiWinPromptScroll $w down; break"
2954    bind $dd.input <Meta-Key-n> "_tkiWinPromptScroll $w down; break"
2955    bind $dd.input <Alt-Key-n> "_tkiWinPromptScroll $w down; break"
2956
2957    set dd $w.buts; frame $dd
2958    if { $tki(showButtonsB) } { pack $dd -after $w.s -side top -fill x }
2959
2960    pack [button $dd.next -width 2 -text "Next"] \
2961            -side left -expand 1 -fill both
2962    bindtags $dd.next [list balloon $dd.next Button all]
2963    _tkiBindToButton $w $dd.next next
2964
2965    pack [button $dd.prev -width 2 -text "Previous"] \
2966            -side left -expand 1 -fill both
2967    bindtags $dd.prev [list balloon $dd.prev Button all]
2968    _tkiBindToButton $w $dd.prev prev
2969
2970    pack [button $dd.up -width 2 -text "Up"] \
2971            -side left -expand 1 -fill both
2972    bindtags $dd.up [list balloon $dd.up Button all]
2973    _tkiBindToButton $w $dd.up up
2974
2975    pack [button $dd.last -width 2 -text "Last"] \
2976            -side left -expand 1 -fill both
2977    bindtags $dd.last [list balloon $dd.last Button all]
2978    _tkiBindToButton $w $dd.last last
2979
2980    pack [button $dd.top -width 2 -text "Top"] \
2981            -side left -expand 1 -fill both
2982    bindtags $dd.top [list balloon $dd.top Button all]
2983    set balloonHelp($dd.top) "Go to this info file's
2984topmost info node which
2985has the table of contents."
2986    _tkiBindToButton $w $dd.top top
2987
2988    pack [button $dd.dir  -width 2 -text "Dir"] \
2989            -side left -expand 1 -fill both
2990    bindtags $dd.dir [list balloon $dd.dir Button all]
2991    set balloonHelp($dd.dir) "Go to directory
2992node which lists
2993all info files."
2994    _tkiBindToButton $w $dd.dir dir otherdir "otherdir redirect"
2995
2996    _tkiWinBind $w
2997
2998    #frame $w.main.text.sep -borderwidth 1 -relief sunken -width 150 -height 2
2999
3000    # Fix display styles for search matches and highlighted links.
3001    set tw $w.main.text
3002
3003    case $tki(searchlook) {
3004        inverse {
3005          $tw tag conf searchkey -foreground [lindex [$tw conf -background] 4] \
3006                  -background [lindex [$tw conf -foreground] 4]
3007        }
3008        color {
3009          $tw tag conf searchkey -foreground $tki(searchColor)
3010        }
3011        font {
3012          $tw tag conf searchkey -font $tki(searchFont)
3013        }
3014    }
3015
3016    case $tki(highlight) {
3017        inverse {
3018            if { $tki(linklook) == "color" } {
3019                $tw tag conf cursor \
3020                        -foreground [lindex [$tw conf -background] 4] \
3021                        -background $tki(linklookColor)
3022            } else {
3023                $tw tag conf cursor \
3024                        -foreground [lindex [$tw conf -background] 4] \
3025                        -background [lindex [$tw conf -foreground] 4]
3026            }
3027        }
3028        color {
3029            $tw tag conf cursor -foreground $tki(highlightColor)
3030        }
3031        font {
3032            $tw tag conf cursor -font $tki(highlightFont)
3033        }
3034    }
3035
3036    set tki(curWindow) $w
3037    wm geometry $w $tki(geometry)
3038    if { $tki(iconic) == 1 } {
3039        wm iconify $w; set tki(iconic) 0
3040    }
3041    return $w
3042}
3043
3044#
3045# What to do if a menu is torn off; this is not used under Tk8.0 since
3046# torn-off menus under Tk8.0 are synchronized automatically.
3047#
3048proc _tkiMenuTearOff {w menu tornMenu} {
3049   case $menu {
3050       "*.history.m" {
3051           upvar #0 $w wvars
3052           lappend wvars(historyMenus) $tornMenu
3053       }
3054       default {
3055           # no entry of a torn off menu should be disabled.
3056           set numentries [$tornMenu index end]
3057           for {set idx 0} {$idx <= $numentries} {incr idx} {
3058               if {[$tornMenu type $idx] == "command"} {
3059                   $tornMenu entryconf $idx -state normal
3060               }
3061           }
3062       }
3063   }
3064   $tornMenu add separator
3065   $tornMenu add command -label "Close Menu" -command "destroy $tornMenu"
3066}
3067
3068##############################################################################
3069#
3070#		Utility functions for updating info windows
3071#
3072##############################################################################
3073
3074#
3075# Removes all empty lines in window $w starting at index $idx.
3076# This is more subtle than one might think.  Note that the text index
3077# "+1line" wont work on the last line of text, because the newline is
3078# considered part of the previous line.  Thus we use "lineend" instead.
3079#
3080
3081proc _tkiTextTrim { w idx } {
3082    while 1 {
3083	set nidx [$w index "$idx lineend"]
3084	if { [string trim [$w get $idx $nidx]] != "" || [$w index end] == "1.0" } break
3085	$w delete $idx "$nidx +1char"
3086    }
3087}
3088
3089# Modified version of ouster's version
3090proc _tkiTextInsertWithTags { w index text args } {
3091    set start [$w index $index]
3092    $w insert $start $text
3093    foreach tag $args {
3094    	$w tag add $tag $start insert
3095    }
3096}
3097
3098proc _tkiLinkLookTag { tw tag } {
3099    global tki
3100    case $tki(linklook) {
3101      color { $tw tag conf $tag -fore $tki(linklookColor) }
3102      underline { $tw tag conf $tag -underline 1 }
3103      font { $tw tag conf $tag -font $tki(linklookFont) }
3104    }
3105    $tw tag bind $tag <Enter> [list $tw configure -cursor $tki(linkCursor)]
3106    $tw tag bind $tag <Leave> "_tkiLeaveLink $tw"
3107}
3108
3109#
3110# Add info about the currently displayed node to the window's history
3111# list wvars(history) and to the History menu. Return the new history
3112# list, but don't change wvars(history). If noadd == 1, then don't
3113# change the History menu either. Don't do anything if the currently
3114# displayed node is (oldinfo,oldfileKey).
3115#
3116
3117proc _tkiWinHistoryAdd { w oldinfo oldfileKey {noadd 0}} {
3118    global tki; upvar #0 $w wvars
3119
3120    set fileKey $wvars(fileKey)
3121    if { $fileKey == "" } {
3122        return $wvars(history)
3123    }
3124
3125    set nodeinfo $wvars(nodeinfo)
3126
3127    if { $fileKey == $oldfileKey && $nodeinfo == $oldinfo } {
3128        return $wvars(history)
3129    }
3130
3131    set topline [$w.main.text index @0,0]
3132    set cursorInfo $wvars(cursorInfo)
3133    set node [lindex $nodeinfo 1]
3134
3135    set result [linsert $wvars(history) 0 \
3136           [list $wvars(nodeSpec) $node $fileKey $topline $cursorInfo]]
3137
3138    # Remove doubles:
3139    for {set idx 1} {$idx < [llength $result]} {incr idx} {
3140        set entry [lindex $result $idx]
3141        if {[lindex $entry 1] == $node && [lindex $entry 2] == $fileKey} {
3142            set result [lreplace $result $idx $idx]
3143            break
3144        }
3145    }
3146
3147    # Cut history list down to appropriate length:
3148    if {[llength $result] > $tki(historyLength)} {
3149        set result [lreplace $result end end]
3150    }
3151
3152    if { $noadd == 0 } {
3153        _tkiCreateHistory $w $result
3154    }
3155
3156    return $result
3157}
3158
3159#
3160# Make a new menu $w.history.m from $list
3161#
3162proc _tkiCreateHistory { w list } {
3163    upvar #0 $w wvars
3164
3165    foreach hm $wvars(historyMenus) {
3166        if {![winfo exist $hm]} {continue}
3167        set end [$hm index end]
3168        # Is the menu transient or torn off?
3169        if { [$hm cget -tearoff] } {
3170            set startidx 1
3171        } else {
3172            set startidx 0
3173        }
3174        if { [$hm type end] == "command" && [$hm entrycget end -label] == "Close Menu"} {
3175            set endidx [expr $end - 2]
3176        } else {
3177            set endidx $end
3178        }
3179        $hm del $startidx $endidx
3180        set menuidx [expr $startidx - 1]
3181        set idx 0
3182        foreach entry $list {
3183            incr idx
3184            incr menuidx
3185            set nodespec [lindex $entry 0]
3186            set node [lindex $entry 1]
3187            set fileKey [lindex $entry 2]
3188            set topline [lindex $entry 3]
3189            set cursorInfo [lindex $entry 4]
3190            if { $idx < 36 } {
3191                if { $idx < 10 } {
3192                    set label $idx
3193                } else {
3194                    set label [format "%c" [expr $idx + 55]]
3195                }
3196                $hm insert $menuidx command -label "$label  $nodespec" -und 0 \
3197                        -command [list _tkiJumpTo $w $node $fileKey $topline $cursorInfo]
3198            } else {
3199                $hm add command -label "    $nodespec" \
3200                        -command [list _tkiJumpTo $w $node $fileKey $topline $cursorInfo]
3201            }
3202        }
3203    }
3204}
3205
3206#
3207# Jump to the specified node, to the specified line, and restore the
3208# specified cursorInfo
3209#
3210proc _tkiJumpTo { w node fileKey topline cursorInfo } {
3211    tkiWinShow $node $fileKey $w
3212    $w.main.text yview $topline
3213    tkiHighlightCursor $w $cursorInfo
3214}
3215
3216
3217proc tkiWinDpy { w fileKey info body } {
3218    global tki balloonHelp; upvar #0 $w wvars
3219
3220    #add info about last node to history list:
3221    set wvars(history) [_tkiWinHistoryAdd $w $info $fileKey]
3222
3223    #add info about last node to wvars(lastNodes)
3224    set wvars(lastNodes) [_tkiLastInfo $w $info $fileKey]
3225
3226
3227    set wvars(fileKey) $fileKey
3228    set wvars(nodeinfo) $info
3229    set wvars(lastDir) [file dirname [lindex $tki(fileinfo-$fileKey) 2]]
3230
3231    if { $tki(showDirB) == "1" ||
3232            ( [llength $tki(dirs)] > 1 && [lindex $info 2] == "dir" )} {
3233        set dir $wvars(lastDir)
3234        if { $dir == "." } {
3235            set dir ""
3236        } else {
3237            set dir "${dir}/"
3238        }
3239    } else {
3240        set dir ""
3241    }
3242    set filename [lindex $info 2]
3243    # Now strip the suffix:
3244    foreach suffix $tki(infoSuffix) {
3245        if {$suffix != ""} {
3246            set idx [string last $suffix $filename]
3247            if { $idx != -1 } {
3248                if { [string length $filename] - $idx == [string length $suffix]} {
3249                    set filename [string range $filename 0 [expr $idx - 1]]
3250                    break
3251                }
3252            }
3253        }
3254    }
3255    set wvars(nodeSpec) "(${dir}$filename)[lindex $info 1]"
3256
3257    set wvars(scrollForwardHitBottom) 0
3258    set wvars(scrollBackwardHitTop) 0
3259    set wvars(inSearch) 0
3260    set wvars(cursorInfo) ""
3261
3262    set nodeIdx [lindex $info 0]
3263    set nodeName [lindex $info 1]
3264    tkiStatus "Formatting $wvars(nodeSpec)..." $w 0
3265    set tw $w.main.text
3266#    $tw conf -cursor $tki(waitCursor)
3267#    $w conf -cursor $tki(waitCursor)
3268    set menuidx -1
3269    set menuidx [string first "\n* Menu:" $body]
3270    if { $menuidx > 0 } {
3271        set menutext [string range $body [expr {$menuidx+1}] end]
3272        set beforemenu [string range $body 0 $menuidx]
3273    }
3274
3275    $tw conf -state normal
3276    $tw delete 1.0 end
3277    #
3278    # Insert the body text and add the crossref tags
3279    #
3280    if { $menuidx > 0 } {
3281       $tw insert end $beforemenu
3282       _tkiTextInsertWithTags $tw end $menutext menu
3283    } else {
3284       $tw insert end $body
3285    }
3286    if { [info exist tki(xrefinfo-$fileKey-$nodeIdx)] } {
3287	set xrefinfo $tki(xrefinfo-$fileKey-$nodeIdx)
3288    } else {
3289        set xrefinfo [tkiTimeStatus "Parsing $nodeIdx body" 0 \
3290	  tkiNodeParseBody $nodeIdx $fileKey $body]
3291    }
3292    set ms "1.0"
3293    $tw tag delete xrefkey
3294    foreach xi $xrefinfo {
3295	# xi = { xrefidx toNode startIdx endIdx label}
3296	set xrefidx [lindex $xi 0]
3297	set toNode [lindex $xi 1]
3298        $tw tag add xrefkey "$ms+[lindex $xi 2] c" "$ms +[lindex $xi 3] c"
3299        $tw tag add xref$xrefidx "$ms +[lindex $xi 2] c" "$ms +[lindex $xi 3] c"
3300        # We memorize the position where a button is pressed; if it is
3301        # released far away, we won't enable the associated action
3302        # (chances are, that the user wanted to select or drag)
3303        $tw tag bind xref$xrefidx <Button-1> \
3304             "set tki(x) %x; set tki(y) %y"
3305        $tw tag bind xref$xrefidx <ButtonRelease-1> \
3306             [list _tkiButtonRelease1 $w %x %y $xrefidx $toNode $fileKey xref]
3307        $tw tag bind xref$xrefidx <Button-2> \
3308             "set tki(y) %y"
3309        # The next one is really wild... $toNode can contain backslashes and
3310        # stuff. I didn't know how to do it more elegantly --A.B.
3311        $tw tag bind xref$xrefidx <ButtonRelease-2> \
3312             "[list _tkiButtonRelease2 $w %y $xrefidx $toNode $fileKey xref]
3313              break"
3314        $tw tag bind xref$xrefidx <Shift-ButtonRelease-1> \
3315              "[list _tkiShiftButtonRelease1 $w $xrefidx $toNode $fileKey xref]
3316               break"
3317        $tw tag bind xref$xrefidx <Control-ButtonRelease-1> \
3318               "[list _tkiShiftButtonRelease1 $w $xrefidx $toNode $fileKey xref]
3319                break"
3320        # We need to disable the transient-menu function of button-3
3321        # on tags. Simply binding <Button-3> to "break" does not work:
3322        # the text widget bindings would still be executed. We use a
3323        # global variable tki(breakBindings): if it is 1, the script
3324        # from the text widget binding is not allowed to execute.
3325        $tw tag bind xref$xrefidx <Button-3> "set tki(breakBindings) 1"
3326        $tw tag bind xref$xrefidx <ButtonRelease-3> \
3327                [list _tkiButtonRelease3 $w $xrefidx $toNode $fileKey xref]
3328    }
3329    _tkiLinkLookTag $tw xrefkey
3330
3331    set wvars(xrefinfo) $xrefinfo
3332
3333    _tkiTextTrim $tw 1.0
3334    if { ! $tki(rawHeadersB) } {
3335	$tw delete 1.0 "1.0 +1line"
3336        _tkiTextTrim $tw 1.0
3337    }
3338
3339    #
3340    # Now add the menu tags
3341    #
3342    if { [info exist menutext] } {
3343        if { [info exist tki(menuinfo-$fileKey-$nodeIdx)] } {
3344	  set menuinfo $tki(menuinfo-$fileKey-$nodeIdx)
3345        } else {
3346          set menuinfo [tkiTimeStatus "Parsing $nodeIdx menu" 0 \
3347	    tkiNodeParseMenu $nodeName $nodeIdx $fileKey $body]
3348        }
3349        $tw tag delete menukey
3350        foreach mi $menuinfo {
3351	  # mi = { lineidx menuidx toNode nBeg nEnd label }
3352	  set lineidx [lindex $mi 0]
3353	  set menuidx [lindex $mi 1]
3354	  set toNode [lindex $mi 2]
3355	  set ms "menu.first +$lineidx lines -2 lines"
3356	  $tw tag add menukey "$ms +[lindex $mi 3] c" "$ms +[lindex $mi 4] c +1 c"
3357          $tw tag add menu$menuidx "$ms linestart" "$ms +[lindex $mi 4] c +1 c"
3358          # We memorize the position where a button is pressed; if it is
3359          # released far away, we won't enable the associated action
3360          # (chances are, that the user wanted to select or drag)
3361          $tw tag bind menu$menuidx <Button-1> \
3362               "set tki(x) %x; set tki(y) %y"
3363	  $tw tag bind menu$menuidx <ButtonRelease-1> \
3364               [list _tkiButtonRelease1 $w %x %y $menuidx $toNode $fileKey menu]
3365          $tw tag bind menu$menuidx <Button-2> \
3366               "set tki(y) %y"
3367          $tw tag bind menu$menuidx <ButtonRelease-2> \
3368               "[list _tkiButtonRelease2 $w %y $menuidx $toNode $fileKey menu]
3369                break"
3370          $tw tag bind menu$menuidx <Shift-ButtonRelease-1> \
3371               "[list _tkiShiftButtonRelease1 $w $menuidx $toNode $fileKey menu]
3372                break"
3373          $tw tag bind menu$menuidx <Control-ButtonRelease-1> \
3374               "[list _tkiShiftButtonRelease1 $w $menuidx $toNode $fileKey menu]
3375                break"
3376          $tw tag bind menu$menuidx <Button-3> "set tki(breakBindings) 1"
3377          $tw tag bind menu$menuidx <ButtonRelease-3> \
3378               [list _tkiButtonRelease3 $w $menuidx $toNode $fileKey menu]
3379        }
3380        _tkiLinkLookTag $tw menukey
3381
3382	set wvars(menuinfo) $tki(menuinfo-$fileKey-$nodeIdx)
3383    } else {
3384	catch {unset wvars(menuinfo)}
3385    }
3386
3387    #
3388    # Window titles and status messages
3389    #
3390    $w.s.filename conf -text $wvars(nodeSpec)
3391    $w conf -cursor $tki(normCursor)
3392    wm title $w "$wvars(title): $wvars(nodeSpec)"
3393    wm iconname $w "$wvars(title): $wvars(nodeSpec)"
3394
3395    #
3396    # Disable buttons and menu entries if necessary
3397    #
3398    set toNode [lindex $info 3]
3399    if {$toNode == ""} {
3400        $w.buts.up conf -state disabled
3401        $w.bar.node.m entryconf Up* -state disabled
3402        $w.transientmenu entryconf Up* -state disabled
3403    } else {
3404        set balloonHelp($w.buts.up) "Go to that info node which
3405contains this one as a menu entry.
3406That is the node \"$toNode\"."
3407        $w.buts.up conf -state normal
3408        $w.bar.node.m entryconf Up* -state normal
3409        $w.transientmenu entryconf Up* -state normal
3410    }
3411    set toNode [lindex $info 4]
3412    if {$toNode == ""} {
3413        $w.buts.prev conf -state disabled
3414        $w.bar.node.m entryconf Prev* -state disabled
3415        # $w.transientmenu entryconf Prev* -state disabled
3416    } else {
3417set balloonHelp($w.buts.prev) "Go to previous section on
3418the current hierarchical level.
3419That is the node \"$toNode\"."
3420        $w.buts.prev conf -state normal
3421        $w.bar.node.m entryconf Prev* -state normal
3422        # $w.transientmenu entryconf Prev* -state normal
3423    }
3424    set toNode [lindex $info 5]
3425    if {$toNode == ""} {
3426        $w.buts.next conf -state disabled
3427        $w.bar.node.m entryconf Next* -state disabled
3428        $w.transientmenu entryconf Next* -state disabled
3429    } else {
3430set balloonHelp($w.buts.next) "Go to next section on the
3431current level, i.e. skip all menu entries.
3432That is the node \"$toNode\"."
3433        $w.buts.next conf -state normal
3434        $w.bar.node.m entryconf Next* -state normal
3435        $w.transientmenu entryconf Next* -state normal
3436    }
3437
3438
3439    $w.bar.search.m entryconf "Continue forward search" -state disabled
3440    $w.bar.search.m entryconf "Continue backward search" -state disabled
3441    $w.bar.search.m entryconf "Continue index lookup" -state disabled
3442    if { [llength $wvars(history)] > 0 } {
3443        $w.bar.history conf -state normal
3444    } else {
3445        $w.bar.history conf -state disabled
3446    }
3447
3448    if { [llength $wvars(lastNodes)] >= 1 } {
3449        $w.buts.last conf -state normal
3450        $w.bar.node.m entryconf "Back*"  -state normal
3451        $w.transientmenu entryconf "Back*" -state normal
3452        set balloonHelp($w.buts.last) "Go back to the last node
3453you visited before coming here.
3454That is the node \"[lindex [lindex $wvars(lastNodes) [expr [llength $wvars(lastNodes)] - 1]] 1]\"."
3455    } else {
3456        $w.buts.last conf -state disabled
3457        $w.bar.node.m entryconf "Back*"  -state disabled
3458        $w.transientmenu entryconf "Back*" -state disabled
3459    }
3460    if { $menuidx > 0 } {
3461        $w.bar.node.m entryconf "Menu*" -state normal
3462    } else {
3463        $w.bar.node.m entryconf "Menu*" -state disabled
3464    }
3465    if { $xrefinfo == "" } {
3466        $w.bar.node.m entryconf "Cross*" -state disabled
3467    } else {
3468        $w.bar.node.m entryconf "Cross*" -state normal
3469    }
3470    if { [lindex $wvars(nodeinfo) 1] == "Top" } {
3471        $w.buts.top conf -state disabled
3472        $w.bar.node.m entryconf "Top" -state disabled
3473    } else {
3474        $w.buts.top conf -state normal
3475        $w.bar.node.m entryconf "Top" -state normal
3476    }
3477
3478    if { [lindex $wvars(nodeinfo) 2] == "dir" } {
3479        $w.buts.dir conf -state disabled
3480        $w.bar.file.m entryconf "Dir*" -state disabled
3481    } else {
3482        $w.buts.dir conf -state normal
3483        $w.bar.file.m entryconf "Dir*" -state normal
3484    }
3485
3486    _tkiFindIndices $fileKey [lindex $wvars(nodeinfo) 2]
3487    $w.bar.search.m entryconf "Index*" -state normal
3488    $w.transientmenu entryconf "Index*" -state normal
3489    set infoFileKey $tki(infoFileKey-$wvars(fileKey))
3490    if { $infoFileKey == $wvars(indexInfoFileKey) && $wvars(indexEntriesIndex) < [expr [llength $wvars(indexEntries)] - 1 ] } {
3491        $w.bar.search.m entryconf "Continue index lookup" -state normal
3492    }
3493
3494
3495    if { [llength $tki(dirs)] > 1 } {
3496        $w.bar.dirs.m del 1 end
3497        foreach pp $tki(dirs) {
3498            if { $wvars(lastDir) == $pp } {
3499                set label "* $pp"
3500            } else {
3501                set label "   $pp"
3502            }
3503            $w.bar.dirs.m add com -label $label \
3504                    -command [list _tkiWinAction $w dir $pp]
3505        }
3506    }
3507
3508
3509    # Clean up the window
3510    $tw mark set insert 1.0
3511    $tw mark set anchor insert
3512    $tw tag remove sel 1.0 end
3513    $tw conf -state disabled
3514
3515    # This is really gross
3516    # focus $tw
3517    # after 1 [list $tw tag remove sel 1.0 end]
3518
3519    tkiStatus "" $w 1
3520
3521}
3522
3523##############################################################################
3524#
3525#	The public interface
3526#
3527##############################################################################
3528
3529
3530#
3531# The argument {w} specified an info window in one of three ways:
3532#   - if empty, a new top-level window will be created and returned.
3533#   - if a window (starts will a ``.''), the window must exist and must have
3534#     been previously obtained using tkiWinCreate() or some variant
3535#     of tkiWinShow().
3536#   - otherwise it is a "window tag", which is arbitrary text that
3537#     must not begin with a ``.''.  Each tag has a unique window associated
3538#     with it that will be created (and re-created) upon demand.
3539#     The tag will also appear in the window title&icon.
3540#
3541
3542proc _tkiWinResolveWinName { w } {
3543    global tki
3544    if { ! [info exist tki] } { tkiInit }
3545    if { $w == "" }			{ return [tkiWinCreate] }
3546    if { [string index $w 0] == "." }	{ return $w }
3547
3548    # It must be a tag: retrieve (or make) the window assoicated with the tag
3549    set tag $w
3550    if { ![info exist tki(wintag-$tag)] } {
3551	set tki(wintag-$tag) [tkiWinCreate "" $tag]
3552    }
3553    set w $tki(wintag-$tag)
3554
3555    # Now see if it still exists: the user might have killed it.  If
3556    # gone, recreate it.
3557    if {![winfo exist $w]} {
3558	tkiWinCreate $w $tag
3559    }
3560    return $w
3561}
3562
3563#
3564# This is the primary entry point of this module.  The argument {nodeSpec}
3565# give the node to show, and may contains a filespec as in (filename)nodename.
3566# If no filename is contained in {nodeSpec}, it will be augmented by
3567# the argument {fileSpec} (if non-empty).  The argument
3568# {w} specifies which info window the node should be displayed in,
3569# as described by _tkiWinResolveWinName() above.
3570#
3571# The return value is a list "nodeRef window" where {nodeRef} is
3572# the internal "handle" to the node given by {nodeSpec} and {fileSpec},
3573# and {window} is the full path of the info window.
3574# If the node couldn't be loaded, the {nodeRef} will be empty.
3575#
3576
3577proc tkiWinShow { nodeSpec {fileSpec ""} {w ""} } {
3578    global tki
3579    set w [_tkiWinResolveWinName $w]
3580    upvar #0 $w wvars
3581    set tki(curWindow) $w
3582    $w.main.text conf -cursor $tki(waitCursor)
3583    $w conf -cursor $tki(waitCursor)
3584    _tkiWinPromptUnmap $w
3585    set nodeRef [tkiGetNodeRef $nodeSpec $fileSpec "" $wvars(lastDir)]
3586    if { $nodeRef == "" } {
3587        #Node couldn't be found
3588        set fmtSpec [tkiFmtNodeSpec $nodeSpec $fileSpec]
3589        if { $nodeSpec != "" && ![string match "(*" $nodeSpec] } {
3590            tkiError "Can't locate info nodes ``$fmtSpec'' and ``($nodeSpec)$tki(topLevelNode)''"
3591        } else {
3592            tkiError "Can't locate the info node ``$fmtSpec''"
3593        }
3594        return [list "" $w]
3595    }
3596
3597    set nodeIdx [lindex $nodeRef 0]
3598    set fileKey [lindex $nodeRef 1]
3599    tkiWinDpy $w $fileKey [lindex $tki(nodesinfo-$fileKey) $nodeIdx] \
3600            [lindex $tki(nodesbody-$fileKey) $nodeIdx]
3601    $w conf -cursor $tki(normCursor)
3602    $w.main.text conf -cursor $tki(normCursor)
3603    raise $w
3604    return [list $nodeRef $w]
3605}
3606
3607#
3608# Get the current info node for {w}, and redisplay it in the window.
3609# This is used whenever the display mode (linklook,etc) is changed.
3610#
3611
3612proc tkiWinRefresh { w } {
3613    global tki; upvar #0 $w wvars
3614    if { $tki(showButtonsB) } {
3615        pack $w.buts -after $w.s -fill x
3616    } else {
3617        pack forget $w.buts
3618    }
3619    if ![info exist wvars(nodeinfo)] return
3620    set nodeinfo $wvars(nodeinfo)
3621    return [tkiWinShow [lindex $nodeinfo 1] $wvars(fileKey) $w]
3622}
3623proc tkiWinRefreshAll { } {
3624    global tki
3625
3626    foreach w $tki(windows) {
3627	if { ![winfo exist $w] } continue
3628	if [catch {tkiWinRefresh $w} error] {
3629	    global errorInfo
3630	    puts stderr "tkInfo: refresh $w: $error\n$errorInfo"
3631	}
3632    }
3633}
3634
3635#
3636# A helper function to provide "context" help.  The idea is that the
3637# application, when it creates each window/widget, creates a global array
3638# variable corresponding to each "key" window in the application.  The
3639# array element "infonodename" contains the node name to display for
3640# context help for that window and its children.
3641#
3642# Start at window {w}, and traverse up the window tree looking for a variable
3643# of the form "$w(infonodename)".  If found, a window displaying that node
3644# will be generated.  {fileSpec} may be used to augment the infonode,
3645# and {infowin} may specific a pre-existing info window returned by
3646# tkiWinShow().
3647#
3648
3649proc tkiWinContextHelp { w {fileSpec ""} {infowin ""} } {
3650    for {} { $w != ""} {set w [winfo parent $w]} {
3651	# Line below is kludgy, b/c I can't see any other way to do it.
3652	if [uplevel #0 [list info exist ${w}(infonodename)]] {
3653	    upvar #0 $w wvars
3654    	    return [tkiWinShow $wvars(infonodename) $fileSpec $infowin]
3655	}
3656    }
3657    if { $fileSpec != "" } {
3658    	return [tkiWinShow Top $fileSpec $infowin]
3659    }
3660    return [tkiWinShow "(builtin)Quick Help" "" $infowin]
3661}
3662
3663##########################################################################
3664# The following material was formerly contained in the file tkicore.tcl:
3665#
3666# This is the core of the tkinfo package.  It handles reading, parsing,
3667# and storing info files.  Everything in here should be tcl-only, no
3668# tk stuff.  Note that this can't be used independently: it requires
3669# initialization and error handling stuff found in tkinfo.tcl.
3670
3671
3672
3673# Get a globally unique serial number.
3674#
3675
3676proc tkiGetSN { } {
3677    global tki
3678    incr tki(sn)
3679    return $tki(sn)
3680}
3681
3682#
3683# Add tcl list of paths {newPaths} to the directory search list.  The
3684# list is added in order at the *head* of the list.  Duplicate paths
3685# are removed, leaving the first of several identical paths in. If
3686# the directory contains an info file "dir", then it is added to
3687# tki(dirs) as well.
3688
3689
3690proc tkiAddInfoPaths { newPaths } {
3691    global tki
3692
3693    if { ! [info exist tki(infoPath) ] } {
3694	set tki(infoPath) ""
3695    }
3696
3697    for {set idx [expr [llength $newPaths] - 1]} {$idx >= 0} {incr idx -1} {
3698        set newPath [lindex $newPaths $idx]
3699        if { $newPath == "" } {continue}
3700        if { ![tkiFileIsAbsolute $newPath] } {
3701            set newPath "./$newPath"
3702        }
3703        if { ![file isdir $newPath] } {continue}
3704        set tki(infoPath) [linsert $tki(infoPath) 0 $newPath]
3705
3706        set dup [lsearch [lrange $tki(infoPath) 1 end] $newPath]
3707        if { $dup < 0 } {
3708            # no duplicate. Check whether it belongs into tki(dirs):
3709            if {[_tkiFileFindSuf "$newPath/dir"] != ""} {
3710                set tki(dirs) [linsert $tki(dirs) 0 $newPath]
3711            }
3712        } else {
3713            # Kill off duplicate
3714	    set tki(infoPath) [lreplace $tki(infoPath) [expr {$dup+1}] [expr {$dup+1}]]
3715	}
3716    }
3717}
3718
3719proc _tkiFileFindSuf { fileName } {
3720    global tki
3721
3722    foreach suf $tki(infoSuffix) {
3723	foreach extrasuf {"" .gz .Z .z .bz2} {
3724	    set filePath "$fileName$suf$extrasuf"
3725	    if { [file isfile $filePath] } {
3726		return $filePath
3727	    }
3728	}
3729    }
3730    return ""
3731}
3732
3733#
3734# Given {fileName} (see intro section above), find the corresponding
3735# filepath.  The filepath of {pntFileKey}, if specified, is
3736# used as a starting point for locating {fileName}.
3737# Returns the file path if found, else empty string.
3738#
3739proc tkiFileFind { fileName {startSearchDir ""} } {
3740    global tki
3741
3742    if { [tkiFileIsAbsolute $fileName] } {
3743	set filePath [_tkiFileFindSuf $fileName]
3744	if { $filePath != "" } { return $filePath }
3745	set filePath [_tkiFileFindSuf [string tolower $fileName]]
3746        return $filePath
3747    } else {
3748	# Try all the infopaths, and all suffixs
3749	foreach prepath "$startSearchDir $tki(infoPath)" {
3750	    set filePath [_tkiFileFindSuf $prepath/$fileName]
3751	    if { $filePath != "" } { return $filePath }
3752	    set filePath [_tkiFileFindSuf $prepath/[string tolower $fileName]]
3753	    if { $filePath != "" } { return $filePath }
3754	}
3755        return ""
3756    }
3757}
3758
3759#
3760# Determines whether filename is an absolute path. Should work also
3761# for names starting with Windows style drive letters.
3762#
3763proc tkiFileIsAbsolute { filename } {
3764    return [regexp -nocase {^(/|\./|\.$|\.\./|\.\.$|~|[a-z]:)} $filename]
3765}
3766
3767#
3768# Given {fileName}, find the corresponding filepath via tkiFileFind().
3769# Return a {fileKey} for the file, and make the appropriate table entries.
3770# Note that {fileName} must be just that, and not a filekey.
3771#
3772proc tkiFileAdd { fileName {pntFileKey ""} {startSearchDir ""} } {
3773    global tki
3774
3775    if {$pntFileKey != ""} {
3776        set startSearchDir [file dirname [lindex $tki(fileinfo-$pntFileKey) 2]]
3777    }
3778    if { [info exist tki(fileKeys-$fileName)] } {
3779        foreach key $tki(fileKeys-$fileName) {
3780            if { [file dirname [lindex $tki(fileinfo-$key) 2]] == $startSearchDir } {
3781                return $key
3782            }
3783        }
3784    } else {
3785        set tki(fileKeys-$fileName) ""
3786    }
3787    set filePath [tkiFileFind $fileName $startSearchDir]
3788    if { $filePath == "" } { return "" }
3789    set fileKey fk[tkiGetSN]
3790    lappend tki(fileKeys-$fileName) $fileKey
3791
3792    set tki(fileinfo-$fileKey)  [list $fileKey $fileName $filePath $pntFileKey]
3793    set tki(incore-$fileKey) 0
3794    return $fileKey
3795}
3796proc tkiFileGet { fileSpec {pntFileKey ""} {startSearchDir ""} } {
3797    global tki
3798
3799    # Is fileSpec a filekey?
3800    if { [info exist tki(fileinfo-$fileSpec)] } {
3801	set fileKey $fileSpec
3802    } else {
3803        set fileKey [tkiFileAdd $fileSpec $pntFileKey $startSearchDir]
3804        if { $fileKey == "" } {
3805            return ""
3806        }
3807    }
3808
3809    set fileinfo $tki(fileinfo-$fileKey)
3810    if { ! $tki(incore-$fileKey) } {
3811        tkiFileLoad $fileKey [lindex $fileinfo 1] [lindex $fileinfo 2]
3812    }
3813    return $fileKey
3814}
3815proc _tkiFileLoadIndirectTbl { fileKey lines } {
3816    global tki
3817
3818    set indirinfos ""
3819    foreach line $lines {
3820	if { $line != "" } {
3821	    set pair [split $line ":"]
3822	    if { [llength $pair] != 2 } {
3823		tkiFileWarning $fileKey "has bad file-indirect line ``$line''"
3824		continue
3825	    }
3826	    set indirKey [tkiFileAdd [lindex $pair 0] $fileKey]
3827	    if { $indirKey == "" } {
3828		tkiError "Can't locate indirect file ``[lindex $pair 0]''."
3829		continue
3830	    }
3831	    set byteOfs [string trim [lindex $pair 1]]
3832	    lappend indirinfos [list $indirKey $byteOfs]
3833	}
3834    }
3835    set tki(indirf-$fileKey) $indirinfos
3836}
3837proc _tkiFileLookupIndir { indirf byte } {
3838    set lastKey ""
3839    foreach fi $indirf {
3840	if { [lindex $fi 1] > $byte } break
3841	set lastKey [lindex $fi 0]
3842    }
3843    return $lastKey
3844}
3845proc _tkiFileLoadTagTbl { fileKey lines } {
3846    global tki
3847
3848    set subkey [lindex $lines 0]
3849    if { $subkey != "(Indirect)" } return
3850    set indirf $tki(indirf-$fileKey)
3851    set indirinfos ""
3852    foreach line [lrange $lines 1 end] {
3853	if { $line =="" } continue
3854	set pair [split $line $tki(nodeByteSep)]
3855	if { [llength $pair] != 2 } {
3856	    tkiFileWarning $fileKey "has bad tag-indirect line ``$line''"
3857	    continue
3858	}
3859	set nodeName [string trim [string range [lindex $pair 0] 5 end]]
3860	set byteOfs [string trim [lindex $pair 1]]
3861	set indirFile [_tkiFileLookupIndir $indirf $byteOfs]
3862	lappend indirinfos [list $nodeName $byteOfs $indirFile]
3863    }
3864    set tki(indirn-$fileKey) $indirinfos
3865}
3866proc tkiFileParseNode { fileKey node } {
3867    global tki
3868
3869    set lines [split $node "\n"]
3870    set keyline [string trim [lindex $lines 1]]
3871    case $keyline {
3872      { {[Ii]ndirect:} } {
3873	_tkiFileLoadIndirectTbl $fileKey [lrange $lines 2 end]
3874	return "IndirectTable"
3875      }
3876      { {[Tt]ag [Tt]able:} } {
3877	_tkiFileLoadTagTbl $fileKey [lrange $lines 2 end]
3878	return "TagTable"
3879      }
3880      { {[Ee]nd [Tt]ag [Tt]able} } {
3881	return "EndTagTable"
3882      }
3883      { {[Ll]ocal [Vv]ariables:} } {
3884	return "LocalVariables"
3885      }
3886    }
3887    # Some screwed up files omit the ``,'' for the file key.
3888    regsub "(File:\[^,\]*)Node:" $keyline "\\1,Node:" keyline
3889    set nodekey ""; set filekey ""
3890    set nextkey ""; set prevkey ""; set upkey ""
3891    foreach key [split $keyline ",\t"] {
3892	set key [string trim $key]
3893        # Note that the linux-doc sgml package produces "Previous:" headers
3894        # instead of "Prev:".
3895	case $key {
3896	  "File:*" { set filekey [string trim [string range $key 5 end]] }
3897	  "Node:*" { set nodekey [string trim [string range $key 5 end]] }
3898	  "Up:*"   { set upkey   [string trim [string range $key 3 end]] }
3899	  "Prev:*" { set prevkey [string trim [string range $key 5 end]] }
3900	  "Previous:*" { set prevkey [string trim [string range $key 9 end]] }
3901	  "Next:*" { set nextkey [string trim [string range $key 5 end]] }
3902	}
3903    }
3904    if { $nodekey == "" } { return "" }
3905    lappend tki(nodesinfo-$fileKey) [list [llength $tki(nodesinfo-$fileKey)] $nodekey $filekey $upkey $prevkey $nextkey]
3906    # We need to get rid of all strange control characters:
3907    regsub -all "\[\a\b\v\f\]" $node "" node
3908    lappend tki(nodesbody-$fileKey) $node
3909    return $nodekey
3910}
3911proc _tkiFileRead {fileName filePath} {
3912    global tki
3913
3914    tkiStatus "Loading $fileName..." "" 0
3915
3916    case $filePath in {
3917      *.Z	{ set fp "|$tki(compresscat-Z) $filePath" }
3918      *.z	{ set fp "|$tki(compresscat-z) $filePath" }
3919      *.gz	{ set fp "|$tki(compresscat-gz) $filePath" }
3920      *.bz2     { set fp "|$tki(compresscat-bz2) $filePath" }
3921      default	{ set fp $filePath }
3922    }
3923    if [catch {open $fp "r"} fid] {
3924	tkiError "Can't open ``$fp''."
3925	return ""
3926    }
3927    set text [read $fid]
3928    close $fid
3929    return $text
3930}
3931proc tkiFileLoad { fileKey fileName filePath {fileText ""}} {
3932    global tki
3933
3934    if { $fileText == "" } {
3935        set fileText [_tkiFileRead $fileName $filePath]
3936    }
3937    if { $fileText == "" } {
3938        return ""
3939    }
3940    set nodelist [split $fileText $tki(nodeSep)]
3941    set nodecnt 0
3942    set tki(nodesinfo-$fileKey) ""
3943    set tki(nodesbody-$fileKey) ""
3944    foreach node $nodelist {
3945        incr nodecnt
3946        if { $nodecnt==1 || [string length $node] < 10 } continue
3947        set nodeName [tkiFileParseNode $fileKey $node]
3948        if { $nodeName == "" } {
3949            puts stdout "Warning: node #$nodecnt of file $filePath is bogus"
3950            continue
3951        }
3952    }
3953    set tki(incore-$fileKey) 1
3954    return $fileKey
3955}
3956
3957#
3958# Parse nodeSpec and fileSpec.  {nodeSpecVar} and {fileSpecVar} must
3959# refer to variables within the caller's context.  They will be substituted
3960# and replaced with canonical forms.
3961#
3962
3963proc tkiParseNodeSpec { nodeSpecVar fileSpecVar } {
3964    global tki
3965    upvar $nodeSpecVar nodeSpec $fileSpecVar fileSpec
3966
3967    if { [string index $nodeSpec 0] == "(" } {
3968	set ridx [string first ")" $nodeSpec]
3969	if { $ridx < 0 } {
3970            set ridx [string length $nodeSpec]
3971	}
3972	set fileSpec [string range $nodeSpec 1 [expr $ridx-1]]
3973	set nodeSpec [string range $nodeSpec [expr $ridx+1] end]
3974    }
3975
3976    if { $nodeSpec == "" } {
3977	set nodeSpec $tki(topLevelNode)
3978        if { $fileSpec == "" } {
3979            set fileSpec "dir"
3980        }
3981    }
3982    set nodeSpec [string trim $nodeSpec]
3983    set fileSpec [string trim $fileSpec]
3984    return 1
3985}
3986
3987proc tkiFmtFileSpec { fileSpec } {
3988    global tki
3989    if [info exist tki(fileinfo-$fileSpec)] {
3990	return [lindex $tki(fileinfo-$fileSpec) 1]
3991    }
3992    return $fileSpec
3993}
3994proc tkiFmtNodeSpec { nodeSpec {fileSpec ""} } {
3995    global tki
3996    if ![tkiParseNodeSpec nodeSpec fileSpec] {
3997	return "Bad file/node spec ``$nodeSpec''"
3998    }
3999    set fileSpec [tkiFmtFileSpec $fileSpec]
4000    return "($fileSpec)$nodeSpec"
4001}
4002
4003#
4004# This is the core search function.  It attempts to locate {nodeSpec}
4005# where ever it is.  {fileSpec} is a default file name that is used
4006# only if {nodeSpec} doesn't contain a reference.
4007# Returns a list {nodeIdx fileKey}, where {nodeIdx} is the index of the
4008# node within {fileKey}.
4009#
4010# As discussed in the intro above, at this level we cannot allow any
4011# concept of "current file" or "current node": it is up to the caller
4012# to maintain that information and pass up the appropriate arguments.
4013#
4014
4015proc tkiGetNodeRef { nodeSpec {fileSpec ""} {pntFileKey ""} {startSearchDir ""}} {
4016    global tki
4017
4018    # Case sensitive search
4019    set nodeRef [_tkiGetNodeRef $nodeSpec $fileSpec $pntFileKey $startSearchDir 0]
4020    if { $nodeRef != "" } {
4021        return $nodeRef
4022    }
4023
4024    # Case insensitive search
4025    set nodeRef [_tkiGetNodeRef $nodeSpec $fileSpec $pntFileKey $startSearchDir 1]
4026    if { $nodeRef != "" } {
4027        return $nodeRef
4028    }
4029
4030    return ""
4031}
4032proc _tkiGetNodeRef { nodeSpec fileSpec pntFileKey startSearchDir caseinsen } {
4033    global tki
4034
4035    # the following may change nodeSpec and fileSpec!
4036    if ![tkiParseNodeSpec nodeSpec fileSpec] {
4037	return ""
4038    }
4039
4040    set fileKey [tkiFileGet $fileSpec $pntFileKey $startSearchDir]
4041    if { $fileKey != "" } {
4042        set fileName [lindex $tki(fileinfo-$fileKey) 1]
4043        tkiStatus "Searching for node ``$nodeSpec'' in $fileName..." "" 0
4044
4045        set realPntKey [lindex $tki(fileinfo-$fileKey) 3]
4046        if { $caseinsen } {
4047            set nodeSpec [string tolower $nodeSpec]
4048        }
4049
4050        # Popup to our indirect-parent, if it has a tag table
4051        if { $pntFileKey == "" && $realPntKey != "" && [info exist tki(indirn-$realPntKey)] } {
4052            return  [_tkiGetNodeRef $nodeSpec $realPntKey "" $startSearchDir $caseinsen]
4053        }
4054
4055        #  Use index on this file, pushdown to our children
4056        if { [info exist tki(indirn-$fileKey)] } {
4057            # Use node index (indirect)
4058            if { $caseinsen } {
4059                foreach indir $tki(indirn-$fileKey) {
4060                    if { $nodeSpec == [string tolower [lindex $indir 0]] } {
4061                        set nodeRef [_tkiGetNodeRef $nodeSpec [lindex $indir 2] $fileKey "" 1]
4062                        if { $nodeRef != "" } { return $nodeRef }
4063                        tkiFileWarning $fileKey "Incorrect tag table"; break
4064                    }
4065                }
4066            } else {
4067                foreach indir $tki(indirn-$fileKey) {
4068                    if { $nodeSpec == [lindex $indir 0] } {
4069                        set nodeRef [_tkiGetNodeRef $nodeSpec [lindex $indir 2] $fileKey "" 0]
4070                        if { $nodeRef != "" } { return $nodeRef }
4071                        tkiFileWarning $fileKey "Incorrect tag table"; break
4072                    }
4073                }
4074            }
4075        } else {
4076            # Brute force on this file
4077            if { [info exist tki(nodesinfo-$fileKey)] } {
4078                if { $caseinsen } {
4079                    foreach nodeinfo $tki(nodesinfo-$fileKey) {
4080                        if { $nodeSpec == [string tolower [lindex $nodeinfo 1]] } {
4081                            return [list [lindex $nodeinfo 0] $fileKey]
4082                        }
4083                    }
4084                } else {
4085                    foreach nodeinfo $tki(nodesinfo-$fileKey) {
4086                        if { $nodeSpec == [lindex $nodeinfo 1] } {
4087                            return [list [lindex $nodeinfo 0] $fileKey]
4088                        }
4089                    }
4090                }
4091            }
4092            # Look for node in all indirect files (brute force)
4093            if { [info exist tki(indirf-$fileKey)] } {
4094                foreach indir $tki(indirf-$fileKey) {
4095                    set nodeRef [_tkiGetNodeRef $nodeSpec [lindex $indir 0] $fileKey "" $caseinsen]
4096                    if { $nodeRef != "" } { return $nodeRef }
4097                }
4098            }
4099        }
4100        # Look for node in my parent, but only if not called from my pnt
4101        if { $pntFileKey == "" && $realPntKey != "" } {
4102            set nodeRef [_tkiGetNodeRef $nodeSpec $realPntKey "" $startSearchDir $caseinsen]
4103            if { $nodeRef != "" } { return $nodeRef }
4104        }
4105
4106        # In case we were called with an info file name of emacs-2 for instance:
4107        if { [info exists tki(nodesinfo-$fileSpec)] } {
4108            set infofile [lindex [lindex $tki(nodesinfo-$fileSpec) 0] 2]
4109            set nodeRef [_tkiGetNodeRef $nodeSpec $infofile $pntFileKey $startSearchDir $caseinsen]
4110            if { $nodeRef != "" } {
4111                return $nodeRef
4112            }
4113        }
4114    }
4115    # This is to support XEmacs-style menus which contain only
4116    # the filename, but not in parentheses. Also, we have gotten such a
4117    # filename on the command line.
4118    if { $nodeSpec != $tki(topLevelNode) } {
4119        set nodeRef [_tkiGetNodeRef $tki(topLevelNode) $nodeSpec "" $startSearchDir $caseinsen]
4120        if { $nodeRef != "" } {
4121            return $nodeRef
4122        }
4123        # If we can't find the node elsewhere, we try the menu entries of (dir)Top
4124        foreach directory $tki(dirs) {
4125            set dirNodeRef [tkiGetNodeRef $tki(topLevelNode) "dir" "" $directory]
4126            if { $dirNodeRef != "" } {
4127                set topNodeIdx [lindex $dirNodeRef 0]
4128                set dirFileKey [lindex $dirNodeRef 1]
4129                if { ![info exist tki(menuinfo-$dirFileKey-$topNodeIdx)] } {
4130                    set body [lindex $tki(nodesbody-$dirFileKey) $topNodeIdx]
4131                    tkiNodeParseMenu $tki(topLevelNode) $topNodeIdx $dirFileKey $body
4132                }
4133                set dirMenu $tki(menuinfo-$dirFileKey-$topNodeIdx)
4134                if {$caseinsen} {
4135                    foreach mi $dirMenu {
4136                        if { [string tolower [lindex $mi 5]] == $nodeSpec } {
4137                            return [tkiGetNodeRef [lindex $mi 2]]
4138                        }
4139                    }
4140                } else {
4141                    foreach mi $dirMenu {
4142                        if { [lindex $mi 5] == $nodeSpec } {
4143                            return [tkiGetNodeRef [lindex $mi 2]]
4144                        }
4145                    }
4146                }
4147            }
4148        }
4149    }
4150    # All efforts failed.
4151    return ""
4152}
4153
4154#
4155# Initialize the regexp strings that are used later in
4156# tkiNodeParseBody() (for xrefs) and tkiNodeParseMenu() (for menus).
4157# This func is called once from tkiInit() and then destroyed.
4158#
4159
4160proc _tkiNodeParseInit { } {
4161    global tki
4162
4163    # For xrefs, there are two forms:
4164    #	*note nodeSpec::terminator			(form 1)
4165    #   *note label: nodeSpec terminator		(form 2)
4166    # Terminator is ``.'' or ``,'', forms may wrap across lines.
4167    set tki(re_xref1_p) "\\*(note\[ \t\n\]*)(\[^:\]+)::"
4168    set tki(re_xref1_s) "x\\1\037e\\2\037fxx"
4169    set tki(re_xref2_p) "\\*(note\[ \t\n\]*)(\[^:\]+)(:\[ \t\n\]*)(\\(\[^ \t\n)\]+\\))?(\[^.,\]*)\[.,\]"
4170    set tki(re_xref2_s) "x\\1\037a\\2\037b\\3\037c\\4\\5\037dx"
4171
4172
4173    # For menus, there are two forms:
4174    #	* nodeSpec::	comments...			(form 1)
4175    #   * label: nodeSpec[\t.,] comments...		(form 2)
4176    set tki(re_menu1_p) "(\\*\[ \t\]*)(\[^:\]+)::"
4177    set tki(re_menu1_s) "\\1\037A\\2\037B"
4178    # rp2 = "* ws label: ws", rp2a="rp2 nodename ws", rp2b="rp2 (file)node ws"
4179    set tki(re_menu2_p) "(\\*\[ \t\]*)(\[^:\]+)(:\[ \t\]*)(\\(\[^ \t)\]+\\))?(\[^\t.,\]*)"
4180    set tki(re_menu2_s) "\\1\037A\\2\037B\\3\037C\\4\\5\037D"
4181}
4182
4183#
4184# Parse a nody-body and return a list of the cross references.
4185# Store the information in tki(xrefinfo-$fileKey-$nodeIdx).
4186#
4187
4188proc tkiNodeParseBody { nodeIdx fileKey bodytext } {
4189    global tki
4190
4191    regsub -all -nocase $tki(re_xref1_p) $bodytext $tki(re_xref1_s) bodytext
4192    regsub -all -nocase $tki(re_xref2_p) $bodytext $tki(re_xref2_s) bodytext
4193    set xrefinfo ""
4194    set curIdx 1
4195    foreach seg [split $bodytext "\037"] {
4196        if { [string index $seg 0] == "a" || [string index $seg 0] == "e" } {
4197            regsub -all "\[ \t\n\]+" "[string range $seg 1 end]" " " label
4198            set stIdx $curIdx
4199        }
4200	set curIdx [expr { $curIdx + [string length $seg] - 1 }]
4201	if { [string index $seg 0] != "c" && [string index $seg 0] != "e" } {
4202            continue
4203        }
4204	set toNode [string range $seg 1 end]
4205	regsub -all "\[ \t\n\]+" $toNode " " toNode
4206	lappend xrefinfo [list [llength $xrefinfo] $toNode $stIdx $curIdx $label]
4207    }
4208    set tki(xrefinfo-$fileKey-$nodeIdx) $xrefinfo
4209    return $xrefinfo
4210}
4211
4212#
4213# Parse the menu and extract the keywords
4214# Store the information in tki(menuinfo-$fileKey-$nodeIdx).
4215#
4216proc tkiNodeParseMenu { nodeName nodeIdx fileKey bodytext } {
4217    global tki
4218
4219    # There are two forms:
4220    #	* nodeSpec::	comments...			(form 1)
4221    #   * label: nodeSpec[ \t.,] comments...		(form 2)
4222    set rp1 $tki(re_menu1_p)
4223    set sp1 $tki(re_menu1_s)
4224    set rp2 $tki(re_menu2_p)
4225    set sp2 $tki(re_menu2_s)
4226
4227    set menuidx [string first "\n* Menu:" $bodytext]
4228    if { $menuidx > 0 } {
4229        set menutext [string range $bodytext [expr {$menuidx+1}] end]
4230    } else {
4231        return ""
4232    }
4233    set menuinfo ""
4234    set linecnt 0; set menucnt 0
4235    foreach line [split $menutext "\n"] {
4236	incr linecnt
4237	if { [string index $line 0] != "*"
4238	  || [string range $line 0 6] == "* Menu:" } continue
4239	if { [regsub $rp1 $line $sp1 prsline] } {
4240	    set nBeg [expr { [string first "\037A" $prsline] + 0 } ]
4241	    set nEnd [expr { [string first "\037B" $prsline] - 3 } ]
4242            set toNode [string range $line $nBeg $nEnd]
4243            regexp "\037A(.*)\037B" $prsline dummy label
4244	} else {
4245	    if { [regsub $rp2 $line $sp2 prsline] } {
4246	        set nBeg [expr { [string first "\037A" $prsline] - 0 } ]
4247	        set nEnd [expr { [string first "\037D" $prsline] - 7 } ]
4248                regexp "\037C(.*)\037D" $prsline dummy toNode
4249                regexp "\037A(.*)\037B" $prsline dummy label
4250	    } else {
4251		tkiFileWarning $fileKey "node $nodeName: bad syntax in line $linecnt of menu"
4252		continue
4253	    }
4254	}
4255	lappend menuinfo [list $linecnt $menucnt $toNode $nBeg $nEnd $label]
4256	incr menucnt
4257    }
4258    set tki(menuinfo-$fileKey-$nodeIdx) $menuinfo
4259    return $menuinfo
4260}
4261
4262#
4263# This is equivalent to $w tag prevrange $tag $start $stop
4264# but this command doesn't exist in tk4.0....
4265# Binary search is probably overkill here.
4266#
4267
4268proc _tkiprevrange {w tag start {stop 1.0}} {
4269    set ranges [$w tag ranges $tag]
4270    if { $ranges == "" } {
4271        return ""
4272    }
4273    set beg 0; set end [expr [llength $ranges] - 2]
4274    while { $end - $beg > 2 } {
4275        set middle [expr int(($beg + $end) / 4) * 2 ]
4276        if [$w compare [lindex $ranges $middle] < $start] {
4277            set beg $middle
4278        } else {
4279            set end $middle
4280        }
4281    }
4282    if { [$w compare [lindex $ranges $beg] >= $start] } {
4283        return ""
4284    } elseif { [$w compare [lindex $ranges $end] < $start] } {
4285        set best $end
4286    } else {
4287        set best $beg
4288    }
4289    if { [$w compare [lindex $ranges $best] > $stop] } {
4290        return [list [lindex $ranges $best] [lindex $ranges [expr $best + 1]]]
4291    } else {
4292        return ""
4293    }
4294}
4295
4296
4297#
4298# Search through w's current info file for pattern, starting with the
4299# node following the current one. Bring up the first node containing
4300# string, and call searchboxSearch on that node. At the end of the
4301# infofile, wrap around to the beginning. If no node contains string,
4302# return 0, else return whatever searchboxSearch returned.  It should
4303# have been checked elsewhere that the regexp actually compiles
4304# correctly.
4305#
4306
4307proc _tkiSearchFileForw {w pattern regexpB casesenB incr} {
4308    global tki; upvar #0 $w wvars
4309
4310    # _tkiLocalMatch is supposed to return 1 iff its argument matches
4311    # $pattern. I don't understand the next lines -- I've found them by
4312    # experimentation --A.B.
4313    if {$regexpB} {
4314        set transformedPattern [_tkiRegexpTransform $pattern]
4315        if {$casesenB} {
4316            proc _tkiLocalMatch {s} [list eval regexp -- [list $transformedPattern] \$s ]
4317        } else {
4318            proc _tkiLocalMatch {s} [list eval regexp -nocase -- [list $transformedPattern] \$s ]
4319        }
4320    } else {
4321        if {$casesenB} {
4322            proc _tkiLocalMatch {s} [list expr \[ string first [list $pattern] \$s \] != -1]
4323        } else {
4324            proc _tkiLocalMatch {s} [list expr \[ string first [list [string tolower $pattern]] \[ string tolower \$s \] \] != -1]
4325        }
4326    }
4327
4328    # Are we currently inside an ongoing search?
4329    if { $wvars(inSearch) && $wvars(searchOriginFileKey) != "" } {
4330        set origFileKey $wvars(searchOriginFileKey)
4331        set origNodeIdx $wvars(searchOriginNodeIdx)
4332    } else {
4333        set origFileKey $wvars(fileKey)
4334        set wvars(searchOriginFileKey) $origFileKey
4335        set origNodeIdx [lindex $wvars(nodeinfo) 0]
4336        set wvars(searchOriginNodeIdx) $origNodeIdx
4337    }
4338
4339    set fileKey $wvars(fileKey)
4340
4341    set pntKey [lindex $tki(fileinfo-$fileKey) 3]
4342    if { $pntKey != "" } {
4343        set fileKeyList $tki(indirf-$pntKey)
4344        set fileKeyListLength [llength $fileKeyList]
4345        for {set idx 0} {$idx < $fileKeyListLength} {incr idx} {
4346            if { [lindex [lindex $fileKeyList $idx] 0] == $fileKey } {
4347                break
4348            }
4349        }
4350        set fileKeyListIdx $idx
4351    }
4352
4353    set nodeIdx [expr [lindex $wvars(nodeinfo) 0] + 1]
4354    set nodeList $tki(nodesinfo-$fileKey)
4355    set nodeListLength [llength $tki(nodesinfo-$fileKey)]
4356
4357    set tki(interrupt) 0
4358
4359    while { $fileKey != $origFileKey || $nodeIdx != $origNodeIdx } {
4360      update
4361      if {$tki(interrupt) == 1} {
4362          tkiStatus "Search for \"$pattern\" interrupted." $w 0
4363          return
4364      }
4365      if { $nodeIdx < $nodeListLength } {
4366        set nodesinfo [lindex $tki(nodesinfo-$fileKey) $nodeIdx]
4367        tkiStatus "Searching for \"$pattern\" in node [lindex $nodesinfo 1]..." $w 0
4368        if {[_tkiLocalMatch [lindex $tki(nodesbody-$fileKey) $nodeIdx]]} {
4369          tkiWinShow [lindex $nodesinfo 1] [lindex $nodesinfo 2] $w
4370          return [searchboxSearch $pattern $regexpB $casesenB searchkey $w ]
4371        }
4372        incr nodeIdx
4373      } else {
4374        set nodeIdx 0
4375        if { $pntKey != "" } {
4376          # Now find next fileKey for current info file and load it into core.
4377          incr fileKeyListIdx
4378          if { $fileKeyListIdx == $fileKeyListLength } {
4379              # wrap around...
4380              set fileKeyListIdx 0
4381          }
4382          set fileKey [lindex [lindex $fileKeyList $fileKeyListIdx] 0]
4383          set fileInfo $tki(fileinfo-$fileKey)
4384          # Don't load if it's already in core!
4385          if { $tki(incore-$fileKey) } {
4386              set nodeList $tki(nodesinfo-$fileKey)
4387              set nodeListLength [llength $nodeList]
4388          } else {
4389              set fileText [_tkiFileRead [lindex $fileInfo 1] [lindex $fileInfo 2]]
4390              if [_tkiLocalMatch $fileText] {
4391                  tkiFileLoad $fileKey [lindex $fileInfo 1] [lindex $fileInfo 2] $fileText
4392                  set nodeList $tki(nodesinfo-$fileKey)
4393                  set nodeListLength [llength $nodeList]
4394              } else {
4395                  set nodeListLength 0
4396              }
4397          }
4398        }
4399      }
4400    }
4401
4402    # Haven't found anything.
4403    if $incr {
4404        tkiBell
4405        set wvars(searchOriginFileKey) ""
4406        tkiStatus "No more matches for \"$pattern\". Back with Ctrl-r." $w 1
4407    } else {
4408        tkiStatus "No matches for \"$pattern\"." $w 1
4409    }
4410    return 0
4411}
4412
4413
4414#
4415# Search backward through w's current info file for pattern, starting
4416# with the node preceding the current one. Bring up the first node
4417# containing string, and call searchboxSearchBackw on that node. At the
4418# beginning of the infofile, wrap around to the end. If no node
4419# contains string, return 0, else return whatever searchboxSearch
4420# returned.  It should have been checked elsewhere that the regexp
4421# actually compiles correctly.
4422#
4423proc _tkiSearchFileBackw {w pattern regexpB casesenB incr} {
4424    global tki; upvar #0 $w wvars
4425
4426    # _tkiLocalMatch is supposed to return 1 iff its argument matches
4427    # $pattern. I don't understand the next lines -- I've found them by
4428    # experimentation --A.B.
4429    if {$regexpB} {
4430        set transformedPattern [_tkiRegexpTransform $pattern]
4431        if {$casesenB} {
4432            proc _tkiLocalMatch {s} [list eval regexp -- [list $transformedPattern] \$s ]
4433        } else {
4434            proc _tkiLocalMatch {s} [list eval regexp -nocase -- [list $transformedPattern] \$s ]
4435        }
4436    } else {
4437        if {$casesenB} {
4438            proc _tkiLocalMatch {s} [list expr \[ string first [list $pattern] \$s \] != -1]
4439        } else {
4440            proc _tkiLocalMatch {s} [list expr \[ string first [list [string tolower $pattern]] \[ string tolower \$s \] \] != -1]
4441        }
4442    }
4443
4444    # Are we currently inside an ongoing search?
4445    if { $wvars(inSearch) && $wvars(searchOriginFileKey) != "" } {
4446        set origFileKey $wvars(searchOriginFileKey)
4447        set origNodeIdx $wvars(searchOriginNodeIdx)
4448    } else {
4449        set origFileKey $wvars(fileKey)
4450        set wvars(searchOriginFileKey) $origFileKey
4451        set origNodeIdx [lindex $wvars(nodeinfo) 0]
4452        set wvars(searchOriginNodeIdx) $origNodeIdx
4453    }
4454
4455    set fileKey $wvars(fileKey)
4456
4457    set pntKey [lindex $tki(fileinfo-$fileKey) 3]
4458    if { $pntKey != "" } {
4459        set fileKeyList $tki(indirf-$pntKey)
4460        set fileKeyListLength [llength $fileKeyList]
4461        for {set idx 0} {$idx < $fileKeyListLength} {incr idx} {
4462            if { [lindex [lindex $fileKeyList $idx] 0] == $fileKey } {
4463                break
4464            }
4465        }
4466        set fileKeyListIdx $idx
4467    }
4468
4469    set nodeIdx [expr [lindex $wvars(nodeinfo) 0] - 1]
4470    set nodeList $tki(nodesinfo-$fileKey)
4471    set nodeListLength [llength $tki(nodesinfo-$fileKey)]
4472
4473    set tki(interrupt) 0
4474
4475    while { $fileKey != $origFileKey || $nodeIdx != $origNodeIdx } {
4476      update
4477      if {$tki(interrupt) == 1} {
4478          tkiStatus "Search for \"$pattern\" interrupted." $w 0
4479          return
4480      }
4481      if { $nodeIdx >= 0 } {
4482        set nodesinfo [lindex $tki(nodesinfo-$fileKey) $nodeIdx]
4483        tkiStatus "Searching for \"$pattern\" in node [lindex $nodesinfo 1]..." $w 0
4484        if {[_tkiLocalMatch [lindex $tki(nodesbody-$fileKey) $nodeIdx]]} {
4485          tkiWinShow [lindex $nodesinfo 1] [lindex $nodesinfo 2] $w
4486          return [searchboxSearchBackw $pattern $regexpB $casesenB searchkey $w]
4487        }
4488        incr nodeIdx -1
4489      } else {
4490        set nodeIdx -1
4491        if { $pntKey != "" } {
4492          # Now find prev fileKey for current info file and load it into core.
4493          if { $fileKeyListIdx == 0 } {
4494              # wrap around...
4495              set fileKeyListIdx $fileKeyListLength
4496          }
4497          incr fileKeyListIdx -1
4498          set fileKey [lindex [lindex $fileKeyList $fileKeyListIdx] 0]
4499          set fileInfo $tki(fileinfo-$fileKey)
4500          # Don't load if it's already in core!
4501          if { $tki(incore-$fileKey) } {
4502              set nodeList $tki(nodesinfo-$fileKey)
4503              set nodeIdx [expr [llength $nodeList] - 1]
4504          } else {
4505              tkiStatus "Searching for \"$pattern\" in file [lindex $fileInfo 1]..." $w 0
4506              set fileText [_tkiFileRead [lindex $fileInfo 1] [lindex $fileInfo 2]]
4507              if [_tkiLocalMatch $fileText] {
4508                  tkiFileLoad $fileKey [lindex $fileInfo 1] [lindex $fileInfo 2] $fileText
4509                  set nodeList $tki(nodesinfo-$fileKey)
4510                  set nodeIdx [expr [llength $nodeList] - 1]
4511              }
4512          }
4513        } else {
4514            set nodeIdx [expr $nodeListLength -1]
4515        }
4516      }
4517    }
4518
4519    # Haven't found anything.
4520    if $incr {
4521        tkiBell
4522        set wvars(searchOriginFileKey) ""
4523        tkiStatus "No more matches for \"$pattern\". Forward with Ctrl-s." $w 1
4524    } else {
4525        tkiStatus "No matches for \"$pattern\"." $w 0
4526    }
4527    return 0
4528}
4529
4530
4531#
4532# This transforms a regexp-style regular expression so that it will
4533# never match more than one line. Most people expect that if they
4534# search for a regexp. Implemented as a state machine.
4535#
4536proc _tkiRegexpTransform {regexp} {
4537    set result ""
4538    set length [string length $regexp]
4539    set state "normal"
4540    for {set idx 0} {$idx < $length} {incr idx} {
4541        set letter [string index $regexp $idx]
4542        case $state {
4543            normal {
4544                case $letter {
4545                    "\\\\\[" {
4546                        set out "\["
4547                        set state "bracket"
4548                    }
4549                    "." {
4550                        set out "\[^\n\]"
4551                        set state "normal"
4552                    }
4553                    "\\\\\\"  {
4554                        set out ""
4555                        set state "backslash"
4556                    }
4557                    "*" {
4558                        set out $letter
4559                        set state "normal"
4560                    }
4561                }
4562            }
4563            backslash {
4564                set out "\\$letter"
4565                set state normal
4566            }
4567            bracket {
4568                case $letter {
4569                    "^" {
4570                        set out "^"
4571                        set state "caret_in_brackets"
4572                    }
4573                    "*" {
4574                        set out $letter
4575                        set state "in_brackets"
4576                    }
4577                }
4578            }
4579            caret_in_brackets {
4580                set out $letter
4581                set state "in_brackets"
4582            }
4583            in_brackets {
4584                case $letter {
4585                    "\\\\\]" {
4586                        set out "\]"
4587                        set state "normal"
4588                    }
4589                    "*" {
4590                        set out $letter
4591                        set state "in_brackets"
4592                    }
4593                }
4594            }
4595        }
4596        set result "${result}$out"
4597    }
4598    if { $state == "backslash"} {
4599        set result "${result}\\"
4600    }
4601    return $result
4602}
4603
4604#
4605# Store a list of the Index nodes in the info file containing filekey
4606# in the global tki(indices-$infoFileKey).
4607# Also locate a list of nodes and store its location in
4608# tki(nodelist-$infoFileKey).
4609#
4610
4611proc _tkiFindIndices {fileKey infoFileName} {
4612    global tki
4613
4614    if { ![info exists tki(infoFileKey-$fileKey)] } {
4615        set infoFileKey "[file dirname [lindex $tki(fileinfo-$fileKey) 2]]/$infoFileName"
4616        set tki(infoFileKey-$fileKey) $infoFileKey
4617    } else {
4618        set infoFileKey tki(infoFileKey-$fileKey)
4619    }
4620
4621
4622    if { [info exists tki(indices-$infoFileKey)] } {
4623        return
4624    } else {
4625        set topnoderef [tkiGetNodeRef $tki(topLevelNode) $fileKey]
4626        if { $topnoderef == "" } {
4627            tkiError "Cannot find top node of $infoFileName"
4628            return 0
4629        }
4630        set topnodefilekey [lindex $topnoderef 1]
4631
4632        # Locate list of nodes:
4633        set parent [lindex $tki(fileinfo-$fileKey) 3]
4634        if {$parent == ""} {
4635            set tki(nodelist-$infoFileKey) [list "nodelistfk" $topnodefilekey]
4636        } else {
4637            set tki(nodelist-$infoFileKey) [list "indirfk" $parent]
4638        }
4639
4640        # Now find Index entries in top node's menu:
4641        set topnodeidx [lindex $topnoderef 0]
4642        if [info exist tki(menuinfo-$topnodefilekey-$topnodeidx)] {
4643            set topmenu $tki(menuinfo-$topnodefilekey-$topnodeidx)
4644        } else {
4645            set topmenu [tkiNodeParseMenu $tki(topLevelNode) $topnodeidx $topnodefilekey [lindex $tki(nodesbody-$topnodefilekey) $topnodeidx]]
4646        }
4647        set result ""
4648        set found 0
4649        foreach entry $topmenu {
4650            if { [regexp -nocase -- "(^|.* )index( .*|\$)" [lindex $entry 5] ] } {
4651                lappend result [list [lindex $entry 2] [lindex $entry 5]]
4652                set found 1
4653            } elseif { $found == 1 } {
4654                break
4655            }
4656        }
4657        set tki(indices-$infoFileKey) $result
4658    }
4659}
4660
4661#
4662# Store a list of the index entries that match the search string in
4663# wvars(indexEntries), update wvars(indexEntriesIndex) and
4664# wvars(indexInfoFileKey).
4665#
4666
4667proc _tkiIndexEntries { w filekey infoFileKey string } {
4668    global tki; upvar #0 $w wvars
4669
4670
4671    set wvars(indexInfoFileKey) $infoFileKey
4672    set wvars(indexString) $string
4673
4674    if { $string == "" } {
4675        set wvars(indexEntries) $tki(indices-$infoFileKey)
4676        set wvars(indexEntriesIndex) 0
4677        return ""
4678    }
4679
4680
4681    set result1 ""
4682    set result2 ""
4683    set result3 ""
4684    set searchstring [string tolower $string]
4685
4686    foreach indexlist $tki(indices-$infoFileKey) {
4687        set index [lindex $indexlist 0]
4688        set indexref [tkiGetNodeRef $index $filekey]
4689        if {$indexref == ""} {
4690            set wvars(indexEntries) ""
4691            return
4692        }
4693        set indexfilekey [lindex $indexref 1]
4694        set indexnodeidx [lindex $indexref 0]
4695        if [info exist tki(menuinfo-$indexfilekey-$indexnodeidx)] {
4696            set indexmenu $tki(menuinfo-$indexfilekey-$indexnodeidx)
4697        } else {
4698            set indexmenu [tkiNodeParseMenu $index $indexnodeidx $indexfilekey [lindex $tki(nodesbody-$indexfilekey) $indexnodeidx]]
4699        }
4700        foreach entry $indexmenu {
4701            set label [lindex $entry 5]
4702            set labellc [string tolower [lindex $entry 5]]
4703            set node [lindex $entry 2]
4704            if { $searchstring == $labellc } {
4705                lappend result1 [list $node $label]
4706            } else {
4707                set idx [string first $searchstring $labellc]
4708                if { $idx == 0 } {
4709                    lappend result2 [list $node $label]
4710                } elseif { $idx > 0 } {
4711                    lappend result3 [list $node $label]
4712                }
4713            }
4714        }
4715    }
4716    tkiStatus "Searching for relevant index entries..." $w 0
4717
4718    set nodesfk [lindex $tki(nodelist-$infoFileKey) 1]
4719    case [lindex $tki(nodelist-$infoFileKey) 0] {
4720        "indirfk" {
4721            set nodelist $tki(indirn-$nodesfk)
4722            set index 0
4723        }
4724        "nodelistfk" {
4725            set nodelist $tki(nodesinfo-$nodesfk)
4726            set index 1
4727        }
4728    }
4729    foreach entry $nodelist {
4730        set labellc [string tolower [lindex $entry $index]]
4731        set node [lindex $entry $index]
4732        if { $searchstring == $labellc } {
4733            lappend result1 [list $node $node]
4734        } else {
4735            set idx [string first $searchstring $labellc]
4736            if { $idx == 0 } {
4737                lappend result2 [list $node $node]
4738            } elseif { $idx > 0 } {
4739                lappend result3 [list $node $node]
4740            }
4741        }
4742    }
4743
4744    set result [concat $result1 $result2 $result3]
4745    # Now remove doubles:
4746    set final ""
4747    set length [llength $result]
4748    for {set i 0} { $i < $length } {incr i} {
4749        set node [lindex [lindex $result $i] 0]
4750        set unique 1
4751        for {set j 0} { $j < $i } {incr j} {
4752            if { [lindex [lindex $result $j] 0] == $node } {
4753                set unique 0; break
4754            }
4755        }
4756        if {$unique} {lappend final [lindex $result $i]}
4757    }
4758    set wvars(indexEntries) $final
4759    set wvars(indexEntriesIndex) 0
4760    return
4761}
4762
4763
4764##########################################################################
4765# The following material was formerly contained in searchbox.tcl:
4766
4767#
4768# SearchBox mega widget
4769#   incremental and regular expression searching in a text widget
4770#
4771#   by Tom Phelps (phelps@cs.Berkeley.EDU)
4772#
4773# extracted from and then used by TkMan and NBT 6-Aug-93
4774#
4775# 19-Aug  made more robust (Kennard White)
4776# 5-Nov-97 heavily lobotomized (Axel Boldt)
4777
4778# requires: proc regexpTextSearch
4779# name space use: prefixes searchbox, sb
4780
4781
4782#--------------------------------------------------
4783#
4784# searchboxSearch -- initiate a search
4785#
4786# params
4787#    str = string to search for
4788#    regexp = boolean - regular expression search?
4789#    casesen = case sensitive?
4790#    tag = tag to associate with matches
4791#       (do a `tag bind' in the text widget for this tag)
4792#    w = text widget
4793#
4794# returns: number of matches found, or -1 if error occured.
4795#--------------------------------------------------
4796
4797proc searchboxSearch {str regexp casesen tag w} {
4798   upvar #0 $w wvars
4799
4800   set tw $w.main.text
4801   if {$str==""} {
4802      tkiError "Nothing to search for!"
4803      return -1
4804   }
4805
4806   if {$regexp} {set type regexp} {set type ""}
4807   set cnt [${type}TextSearch $tw $str $tag $casesen]
4808   if {$cnt==-1} {tkiError "Malformed regular expression."; return -1}
4809   if {$cnt==0} {return [_tkiSearchFileForw $w $str $regexp $casesen 0]}
4810   set txt "Hit Ctrl-s to search for next \"$str\"."
4811   set wvars(inSearch) 1
4812   $w.bar.search.m entryconf "Continue forward search" -state normal
4813   $w.bar.search.m entryconf "Continue backward search" -state normal
4814
4815   tkiStatus $txt $w 1
4816
4817   # show the first one
4818   searchboxNext $tag $w 0.0
4819   return $cnt
4820}
4821
4822proc searchboxSearchBackw {str regexp casesen tag w} {
4823   upvar #0 $w wvars
4824
4825   set tw $w.main.text
4826   if {$str==""} {
4827      tkiError "Nothing to search for!"
4828      return -1
4829   }
4830
4831   if {$regexp} {set type regexp} {set type ""}
4832   set cnt [${type}TextSearch $tw $str $tag $casesen]
4833   if {$cnt==-1} {tkiError "Malformed regular expression."; return -1}
4834   if {$cnt==0} {return [_tkiSearchFileBackw $w $str $regexp $casesen 0]}
4835   set txt "Hit Ctrl-r to search for previous \"$str\"."
4836   set wvars(inSearch) 1
4837   $w.bar.search.m entryconf "Continue forward search" -state normal
4838   $w.bar.search.m entryconf "Continue backward search" -state normal
4839
4840   tkiStatus $txt $w 1
4841
4842   # show the first one
4843   searchboxPrev $tag $w [$tw index end]
4844   return $cnt
4845}
4846
4847#--------------------------------------------------
4848#
4849# searchboxNext -- show the next match
4850#
4851# params
4852#    tag = tag to search for (see searchboxSearch)
4853#    w = text widget
4854#    next = index to start search; defaults to last visible line
4855#
4856# returns: -1 if there is no next match
4857#--------------------------------------------------
4858proc searchboxNext {tag w {next ""}} {
4859    upvar #0 $w wvars
4860
4861    set tw $w.main.text
4862    if { [$tw tag ranges $tag] == ""} {return 0}
4863    if { $next == ""} {
4864        set next [lindex [_tkiWinVisibleInfo $tw] 1]
4865    }
4866    set tmp [$tw tag nextrange $tag $next]
4867    if { $tmp == "" } {
4868        return -1
4869    } else {
4870        $tw yview -pickplace [lindex $tmp 0]
4871   }
4872}
4873
4874#--------------------------------------------------
4875#
4876# searchboxPrev -- show the previous match
4877#
4878# params
4879#    tag = tag to search for (see searchboxSearch)
4880#    w = text widget
4881#    next = index to start search; defaults to top of window
4882#
4883# returns: -1 if there is no next match
4884#--------------------------------------------------
4885proc searchboxPrev {tag w {next ""}} {
4886    upvar #0 $w wvars
4887
4888    set tw $w.main.text
4889    if { [$tw tag ranges $tag] == ""} {return 0}
4890    set top [$tw index @0,0]
4891    if { $next == ""} {set next $top}
4892    set tmp [_tkiprevrange $tw $tag $next]
4893
4894    if { $tmp == ""} {
4895        return -1
4896    } else {
4897        $tw yview -pickplace [lindex $tmp 0]
4898   }
4899}
4900
4901
4902
4903# swiped from mkTextSearch w
4904#
4905# The utility procedure below searches for all instances of a
4906# given string in a text widget and applies a given tag to each
4907# instance found.
4908# Arguments:
4909#
4910# w -		The window in which to search.  Must be a text widget.
4911# string -	The string to search for.  The search is done using
4912#		exact matching only;  no special characters.
4913# tag -		Tag to apply to each instance of a matching string.
4914# case -        (optional) case sensitive?
4915proc TextSearch {w string tag {case 1}} {
4916   set cnt 0
4917
4918    $w tag remove $tag 0.0 end
4919    scan [$w index end] %d numLines
4920    set l [string length $string]
4921    if {!$case} {set string [string tolower $string]}
4922    for {set i 1} {$i <= $numLines} {incr i} {
4923	set match [$w get $i.0 $i.end]
4924	if {!$case} {set match [string tolower $match]}
4925	if {[string first $string $match] == -1} {
4926	    continue
4927	}
4928	set line $match
4929	set offset 0
4930	while 1 {
4931	    set index [string first $string $line]
4932	    if {$index < 0} {
4933		break
4934	    }
4935	    incr offset $index
4936	    $w tag add $tag $i.[expr $offset] $i.[expr $offset+$l]
4937            $w tag raise $tag
4938            incr cnt
4939	    incr offset $l
4940	    # below bug fix from mkSearch.tcl
4941	    set line [string range $line [expr $index+$l] end]
4942	}
4943    }
4944   return $cnt
4945}
4946
4947
4948# modified to handle regexp's and return # of matches -TAP
4949proc regexpTextSearch {w string tag {case 1}} {
4950   set cnt 0
4951   if {$case} {set case ""} {set case "-nocase"}
4952   if {[catch {regexp -- $string bozomaniac}]} {return -1}
4953
4954    $w tag remove $tag 0.0 end
4955    scan [$w index end] %d numLines
4956
4957    for {set i 1} {$i <= $numLines} {incr i} {
4958      set line [$w get $i.0 $i.end]
4959      set offset 0
4960      while 1 {
4961         if {![eval regexp $case -indices -- {$string} {$line} match]} break
4962         scan $match "%d %d" index iend
4963         $w tag add $tag $i.[expr $offset+$index] $i.[expr $offset+$iend+1]
4964         $w tag raise $tag
4965         set line [string range $line [expr $iend+1] end]
4966         incr offset [expr $iend+1]
4967         incr cnt
4968      }
4969   }
4970   return $cnt
4971}
4972
4973
4974##########################################################################
4975# The following material was formerly contained in topgetopt.tcl:
4976
4977#
4978# The function has "top" prefix b/c it is conceptually part of my "top" library.
4979#
4980# Authors: Kennard White (kennard@ohm.eecs.berkeley.edu)
4981#	   Phil Lapsley (phil@ohm.eecs.berkeley.edu)
4982#
4983# Based on "@(#)getopt.tcl 1.5 12/7/91" by Phil Lapsley
4984#
4985
4986# Simple "getopt" for TCL.
4987#
4988# topgetopt ?-any? ?-all? opt_list arg_list
4989# The proc will process the arguments in {arg_list} according to the
4990# information in {opt_list}.  Processed arguments are passed back
4991# to the caller by setting variables in the caller's proc-environment
4992# (i.e., using upvar).
4993#
4994# option_list is a list of option specs.  Each spec is a 3-tuple:
4995#	{ optname varname mode }
4996# optname is the name of the option to be parsed (without the leading dash).
4997# varname is the name of a tcl variable in the caller's environment.
4998#	If ommitted, the varname defaults to the optname.
4999# mode describes the type of option. If ommitted, it defaults to "single".
5000#	The modes:
5001# 	  single:	sets the variable to the next argument.
5002# 	  append:	lappends the next argument to the variable.
5003#			this allows multiple instances of the same option.
5004#	  boolean:	sets the variable to 0 if the argument prefix is "+"
5005#			 and to 1 of the argument prefix is "-".
5006#
5007# "topgetopt" sets the variables named in the option_list that were
5008# specified in arg_list, and returns the remainder of arg_list after
5009# the first non "-" or "+" option.  If a bad option specifier is
5010# encountered, scanning stops and getopt aborts using error.
5011#
5012# If -all is specified, then everything in arg_list must match an
5013# option in opt_list; that is, there may be no "leftover" arguments.
5014#
5015# If -any is specified, then processing will stop at the first
5016# unmatched option.  That is, the returned list of unprocessed
5017# arguments may contain unregcognized options.
5018#
5019# For example, the option_list:
5020#
5021#	{ min max { file filename } { toplevel toplevel boolean } }
5022#
5023# means that the option "-min value" or "-max value" should set the
5024# variables "min" or "max" to the specified value, and "-file foo.txt"
5025# should set the variable "filename" to foo.txt.  "toplevel"
5026# sets the variable "toplevel", and is a boolean:  the option "-toplevel"
5027# would set the variable "toplevel" to 1, while the option "+toplevel"
5028# would set the variable "toplevel" to 0.
5029#
5030# In typical usage, the caller will first initialize all the option
5031# variables to default values, and then call topgetopt.
5032#
5033
5034proc topgetopt { args } {
5035    set do_all 0
5036    set do_any 0
5037    if { [lindex $args 0] == "-all" } {
5038	set do_all 1
5039	set args [lreplace $args 0 0]
5040    }
5041    if { [lindex $args 0] == "-any" } {
5042	set do_any 1
5043	set args [lreplace $args 0 0]
5044    }
5045    if { [llength $args] != 2 } {
5046	error "topgetopt: programming error: wrong number arguments\n$args"
5047    }
5048    set opt_list [lindex $args 0]
5049    set arg_list [lindex $args 1]
5050
5051    set n [llength $arg_list]
5052    for { set i 0 } { $i < $n } { incr i } {
5053	set arg [lindex $arg_list $i]
5054	set argkey [string index $arg 0]
5055	if { $argkey != "-" && $argkey != "+" } {
5056	    if { $do_all } {
5057		error "Extra arguments after options not allowed: ``$arg''"
5058	    }
5059	    break
5060	}
5061	set argname [string range $arg 1 end]
5062	set matched 0
5063	foreach opt $opt_list {
5064	    if { [lindex $opt 0] == $argname } {
5065		set optlen [llength $opt]
5066		set pntVar pntVar$i
5067		upvar 1 [lindex $opt [expr { ($optlen > 1) ? 1 : 0 }]] $pntVar
5068		# lindex returns empty string for out-of-range
5069		case [lindex $opt 2] {
5070		  b* {
5071		    set $pntVar [expr { $argkey == "-" ? 1 : 0}]
5072		  }
5073		  a* {
5074		    lappend $pntVar [lindex $arg_list [incr i 1] ]
5075		  }
5076	          default {
5077		    set $pntVar [lindex $arg_list [incr i 1] ]
5078		  }
5079		}
5080		set matched 1
5081		break
5082	    }
5083	}
5084	if { $matched == 0 } {
5085	    if { $do_any } {
5086		break
5087	    } else {
5088	        error "No match for argument ``$arg''"
5089	    }
5090	}
5091    }
5092    return [lrange $arg_list $i end]
5093}
5094
5095#########################################################################
5096# Balloon help, by John Haxby <jch@pwd.hp.com>, with slight changes
5097# by Axel Boldt <axelboldt@yahoo.com>.
5098#
5099
5100proc tkiBalloonInit {} {
5101    global tki
5102
5103
5104bind balloon <Enter> {
5105    if { [info exists balloonHelp(%W)] && [%W cget -state] != "disabled"} {
5106        set balloonHelp(%W,after) [after $tki(balloonDelay) {showBalloonHelp %W}]
5107    }
5108}
5109
5110bind balloon <Leave> {
5111    unShowBalloonHelp %W
5112}
5113
5114bind balloon <Any-KeyPress> {
5115    unShowBalloonHelp %W
5116}
5117
5118bind balloon <Any-Button> {
5119    unShowBalloonHelp %W
5120}
5121proc showBalloonHelp {w} {
5122    global tki balloonHelp
5123    if {![info exists balloonHelp($w)] || ! $tki(showBalloonsB) } {
5124        return
5125    }
5126    update idletasks
5127    set curpos [winfo pointerxy $w]
5128    set curwin [eval winfo containing $curpos]
5129    if { $w == $curwin } {
5130        if ![winfo exists .balloon] {
5131            toplevel .balloon
5132            wm overrideredirect .balloon true
5133            pack [label .balloon.l \
5134                    -foreground black \
5135                    -background $tki(balloonBackground) \
5136                    -highlightthickness 1 \
5137                    -highlightbackground black]
5138            wm withdraw .balloon
5139        }
5140        .balloon.l configure -text $balloonHelp($w)
5141        set x [expr [lindex $curpos 0]-14]
5142        set y [expr [lindex $curpos 1]+19]
5143        wm geometry .balloon +$x+$y
5144        # This update is important to have the geometry command take
5145        # effect in all cases (A.B.)
5146        update idletasks
5147        raise .balloon
5148        wm deiconify .balloon
5149    }
5150}
5151proc unShowBalloonHelp {w} {
5152    global balloonHelp
5153    if [info exists balloonHelp($w,after)] {
5154        after cancel $balloonHelp($w,after)
5155        unset balloonHelp($w,after)
5156    }
5157    catch {wm withdraw .balloon}
5158}
5159
5160# end of proc tkiBalloonInit
5161}
5162
5163
5164##########################################################################
5165##########################################################################
5166# Now start the main routines:
5167
5168
5169tkiReset
5170tkiBoot
5171
5172
5173##########################################################################
5174##########################################################################
5175# For emacs:
5176
5177# Local Variables:
5178# mode: tcl
5179# mode: outline-minor
5180# outline-regexp: "proc \\|#!/bin/sh"
5181# End:
5182