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