.ds CF Configuration File .ds CP 2002-2003 .ds TW \'twander\' .ds W3 \'win32all\' .TH twander 1 "TundraWare Inc." .SH NAME twander \- File Browser .SH OVERVIEW Wander around a filesystem executing commands of your choice on selected files and directories. The general idea here is that \*(TW provides GUI facilities for navigating around your filesystem, but .B you define the commands you want available via "Command Definitions" in the \*(CF. If you're new to \*(TW and want to know why this program is better and different than whatever you're using at the moment, take a moment to read the section called .B DESIGN PHILOSOPHY toward the end of this document first. Similarly, if this is the first time you've worked with \*(TW, there is a section near the end of this document entitled .B INSTALLING \*(TW which describes how the program should be installed. .SH SYNOPSIS twander [-cdhqrtv] [startdir] .SH OPTIONS .TP .B startdir Directory in which to begin. (default: directory in which program was started) If this directory does not exist or cannot be opened, \*(TW will display an error message and abort. .TP .B -c path/name of \*(CF Specify the location and name of the configuration file. (default is ~/.twander) If this file does not exist or cannot be opened, \*(TW will display a warning to that effect but continue to run. This is reasonable behavior because \*(TW provides a command to reload the \*(CF without exiting the program (which you would presumably do after fixing the \*(CF problem). .TP .B -d debuglevel Start in debug mode dumping the items specified in the debuglevel. (default: debuglevel=0/debug off) \*(TW is able to selectively dump debugging information to stdout. \'debuglevel\' is understood to be a bitfield in which each bit specifies some kind of debugging information or behavior. \'debuglevel\' can be specified in either decimal or hex (using the form 0x#) formats. The bits in the bitfield are defined as follows: .nf Bit Hex Value Meaning --- --------- ------- 0 0x001 Dump Internal Options & User-Settable Options 1 0x002 Dump User-Defined Variables 2 0x004 Dump Command Definitions 3 0x008 Dump Key Bindings 4 0x010 Display, Do Not Execute, Commands When Invoked 5 0x020 Dump Directory Stack As It Changes 6 0x040 Dump Command History Stack After Command Executes 7 0x080 Dump Contents Of Program Memories As They Change 8 0x100 Dump Contents Of Wildcard Stack As It Changes 9 0x200 Reserved/Unused 10 0x400 Reserved/Unused 11 0x800 Dump Requested Debug Information And Exit Immediately .fi These bits can be combined to provided very specific debugging information. For example, \'-d 0x80f\' will dump (to stdout) all the Internal Options, User-Settable Options, User-Defined Options, Command Definitions, and Key Bindings and then terminate the program. .TP .B -h Print help information on stdout. .TP .B -q Quiet mode - suppresses warnings. (default: warnings on) .TP .B -r Turn off automatic refreshing of directory display. (default: refresh on) Normally \*(TW re-reads and displays the current directory every few seconds to reflect any changes that might have occurred to that directory's contents. This option is useful on slow machines (or slow X connections) and/or when working with very large directories. In this situation, the frequent updating of the \*(TW display can make the program unacceptably slow and unresponsive. In this case you can still force an update manually with the REFRESH function (default assignment is to the Control-l key). .TP .B -t Turn off quoting when substituting built-in variables. (default: quoting on) Anytime \*(TW encounters a reference to one of the built-in variables which do string replacement (DIR, DSELECTION, DSELECTIONS, MEM1-12, PROMPT:, SELECTION, SELECTIONS) in a command, it will replace them with .B double quoted strings. This is necessary because any of these can return values which have embedded spaces in them. By quoting them, they can be passed to a command or script as a single item. The -t option disables this behavior and replaces the built-in variable with unquoted literals. .TP .B -v Print detailed version information. .SH OTHER WAYS TO SET \*(TW OPTIONS In addition to these command line options, there are two other ways you can set \*(TW program features. If you prefer, you can set the command line options via the environment variable, TWANDER. That way you don't have to type them in each time you start the program. Say you set the environment variable this way on Unix: .nf export TWANDER=-qt .fi From then on, every time you run the program, the -q and -t options would be invoked (No Quoting, No Warnings) just as if you had typed them in on the command line. The second way to set these (and MANY more) Program Options is by setting the appropriate entries in the \*(CF. This is covered later in this document. \*(TW evaluates options in the following order (from first to last: .IP \(bu 4 Internally set default value of options .IP \(bu 4 Options set in the \*(CF .IP \(bu 4 Options set in the TWANDER environment variable .IP \(bu 4 Options set on the command line .P This means, for example, that the environment variable overrides a corresponding setting in the \*(CF, but the command line overrides the environment variable. Furthermore, there are many Program Options which can .B only be set/changed the \*(CF and are not available in either the environment variable or on the command line. This also means that options set on the command line are not read until after the \*(CF has been processed. So, the -q argument on the command line will not inhibit warnings generated during the reading of the \*(CF. This is best done by adding the statement, WARN=False, at the top of the Configuration File. If the \*(CF is reloaded while the program is running (see the READCONF key below), any options set in the file will have the last word. This allows you to edit the \*(CF and have your changes reflected in a running instance of \*(TW, but it also means that the environment variable/command line arguments are ignored after initial program startup. .SH KEYBOARD USE By design, \*(TW allows you to do almost everything of interest using only the keyboard. Various \*(TW features are thus associated with particular keystrokes which are described below. It is also very simple to change the default key assignments with entries in the \*(CF, also described below. .SH NOTES ON KEYBOARD ARROW/KEYPAD BEHAVIOR AND TEXT DIALOG EDITS Generally, the arrow and keypad keys should do what you would expect on the system in question. On Win32 systems, particularly, there ought to be no odd arrow/keypad behavior. X-Windows is somewhat more problematic in this area. Just what an arrow key is "supposed" to do depends on how it's been mapped in your X server software. Testing \*(TW on various X servers showed quite a bit of variability in how they handled the arrows and keypad. So ... if you're running in an X Windows universe and arrows or keypad do nothing, or do strange things, look into your key maps, don't blame \*(TW. There are several features of \*(TW that will present the user a text entry dialog. These include the CHANGEDIR and RUNCMD features as well as the [PROMPT:...] Built-In Variable (all described below). Any time you are entering text in such a dialog, be aware that the text can be edited several ways - You can edit it using the arrow/keypad editing assignments which are enabled/normal for your operating system, OR you can use emacs-style commands to edit the text. For instance, Control-a, Control-k will erase the text currently entered in the dialog. .SH DEFAULT KEYBOARD AND MOUSE BINDINGS Here, ordered by category, are the default keyboard and mouse bindings for \*(TW. The general format is: .TP .B Description (Program Function Name) .PD 000 Default Key Assignment .IP Default Mouse Assignment (if any) .PD .P The "Program Function Name" is the internal variable \*(TW uses to associate a particular feature with a particular keystroke or mouse action. You can ignore it unless you intend to override the default key assignments. This use is described below in the section entitled, .B Key Binding Statements. It is important to realize that \*(TW key-bindings are .B case-sensitive. This means that \'Control-b\' and \'Control-B\' are different. This was a conscious design decision because it effectively doubles the number of Control/Alt key combinations available for the addition of future features. The default bindings chosen for \*(TW features are all currently .B lower-case. If your program suddenly stops responding to keyboard commands, check to make sure you don't have CapsLock turned on. .B NOTE: Some \*(TW features are doubled on the mouse. These mouse button assignments are documented below for the sake of completeness. However, .B mouse button assignments cannot be changed by the user, even in the \*(CF. .SS General Program Commands This family of commands controls the operation of \*(TW itself. .TP .B Clear History (CLRHIST) Control-y Clears out various program histories including the All Visited Directories list, the Directory Stack, the Command History, and the last manually-entered values for CHANGEDIR and RUNCMD. The 12 Program Memories are not cleared - they have specially dedicated key bindings for this purpose. .TP .B Decrement Font Size (FONTDECR) Control-[ Decrease font size. .TP .B Increment Font Size (FONTINCR) Control-] Increase font size. These two features allow you to change the display font sizes while \*(TW is running. But, you may not immediately get the results you expect. \*(TW internally keeps track of separate font sizes for the main display, the main menu text, and the help menu text. When you use the two font sizing commands above, \*(TW subtracts or adds 1 to each of these three values respectively. On systems like Win32 using TrueType fonts, this works as you would expect, because every font is effectively available in every size. However, in systems like X-Windows or Win32 using fixed-size fonts, you may have to press these keys repeatedly until \*(TW finds a font matching the requested size. This can also cause some parts of the display to change but not others. Suppose you are running on X-Windows and have specified that the main display is to use a 12 point font, and that menus and help should use 10 point font. Let's also suppose that the next font available larger than 12 point is 16 point. If you press FONTINCR twice, both the menu text and help text will jump to 12 point, but the main display text will remain unchanged. Why? Because pressing FONTINCR twice tells \*(TW to set the main display to 14 point (12+1+1) which does not exist, and the menu and help text to 12 point (10+1+1) which does exist, so that change is visible. The "User-Settable Options" Help Menu displays the font metrics (name, size, weight) you've currently specified. Pressing FONTDECR/FONTINCR changes the size specification and this will be reflected in that menu. However, most systems do some form of "best match" font substitution - if you ask for a font that does not exist, the system will use the "closest matching" font as a substitute. This means the font you see specified in the Help Menu is not necessarily the font you're actually using. You're more likely to run into this when running on a Unix/X-Windows system (where not all the fonts are available in all sizes/weights like they are on Win32 TrueType) as you change the font size with FONTDECR/FONTINCR. Reloading the \*(CF (READCONF) will reset the fonts to either their default values or any font sizes specified in the \*(CF. .TP .B Display Command Menu (MOUSECTX) Right-Mouse-Button Displays a list of all available commands in a pop-up menu near the mouse pointer. If no commands are defined, this feature does nothing at all. This means commands can be invoked one of three ways: Directly via the Command Key defined in the \*(CF, via selection in the Command Menu at the top of the GUI, or via selection from the Command Menu. Win32 users should note that, unlike Windows Explorer, the \*(TW Command Menu does not change the set of currently selected items. It merely provides a list of available commands. This allows the command chosen via the Command Menu to operate on a previously selected set of items. .TP .B Display Directory Menu (MOUSEDIR) Shift-Right-Mouse-Button Displays a list of all the directories visited so far in a pop-up menu near the mouse. This means that you can navigate to a previously visited directory in one of two ways: Via a selection in the Directory Menu at the top of the GUI or via a selection from this pop-up menu. .TP .B Display History Menu (None - Derived internally) Control-Shift-Right-Mouse-Button Displays a list of all commands executed so far (including those entered manually) in a pop-up menu near the mouse pointer. If the Command History is empty, this command does nothing. This means you can repeat a previously entered command via the History Menu or this mouse command. (You can also repeat the last manually entered command by pressing RUNCMD - it will preload its text entry area with the last command you entered by hand.) .TP .B Quit Program (QUITPROG) Control-q Exit the program. .TP .B Re-Read \*(CF (READCONF) Control-r Re-read the \*(CF. This allows you to edit the \*(CF while \*(TW is running and then read your changes in without having to exit the program. This is handy when editing or changing Command Definitions. Program Options are set back to their default each time a \*(CF is about to be read (initially or on reload) just before the \*(CF is parsed. This means commenting out or removing a Program Option Statement (see relevant section below) in the \*(CF and then pressing READCONF causes that option to be reset to its default value. STARTDIR defaults to either its internal default ($HOME or ./) or to the value given in the Environment Variable/Command line. .TP .B Refresh Display (REFRESH) Control-l Re-read the current directory's contents and display it. This is most useful if you have turned off automatic directory refreshing with either the -r command line flag or setting the AUTOREFRESH Program Option to False. .TP .B Toggle Details (TOGDETAIL) Control-t Toggle between detailed and filename-only views of the directory. .TP .B Toggle \*(W3 Features (TOGWIN32ALL) Control-w As described later in this document, \*(TW provides enhanced features for Win32 users who also install Mark Hammond's \*(W3 extensions for Python on Win32. This key binding will toggle those advanced features on- and off. This is useful if you happen to be examining a very large directory. The \*(W3 features, while handy, can be computationally expensive and make updates of a directory with many entries somewhat slow. This toggle is provided as a means to temporarily disable the advanced features when viewing such a directory. .SS Directory Navigation This family of commands controls movement between directories. If you attempt to navigate into a directory that does not exist or which does not have appropriate permissions, \*(TW will display a warning message and remain in the current directory. This is .B unlike the case of a non-existent or unreadable directory specified when the program is first started. In that case, \*(TW reports the error and aborts. .TP .B Change Directory (CHANGEDIR) Control-x This is a shortcut that allows you to directly move to a new directory/path - i.e., Without having to navigate to it. Unless you have set the MAXDIR option to 0, CHANGEDIR keeps track of your last manually entered directory and presents it as a default when you press CHANGEDIR again. You can then move to that directory, edit the string to specify another directory, or delete it and enter an entirely new directory. Directories can be edited with either the arrow and keypad keys defined on your system or by emacs editing commands like Control-a, Control-k, Control-e, and so forth. .TP .B Go To Home Directory (DIRHOME) Control-h If the "HOME" environment variable is defined on your system, this will move you to that directory. If the "HOME" environment variable is not defined, this command will move to the original starting directory. .TP .B Go Back One Directory (DIRBACK and MOUSEBACK) .PD 000 Control-b .IP Control-DoubleClick-Left-Mouse-Button .PD \*(TW keeps track of every directory visited and the order in which they are visited. This command allows you to move back successively until you get to the directory in which you started. This feature is implemented as a stack - each "backing up" removes the directory name from the visited list. The "Directory" menu (see .B MENU OPTIONS below) implements a similar feature in a different way and keeps track of all directories visited regardless of order. .TP .B Go To Root Directory (DIRROOT) Control-j Go to the root directory. .TP .B Go To Starting Directory (DIRSTART) Control-s Go back to the original directory in which \*(TW was started. .TP .B Go Up To Parent Directory (DIRUP and MOUSEUP) .PD 000 Control-u .IP Control-DoubleClick-Right-Mouse-Button .PD Move to the parent of the current directory (".."). .TP .B Display Drive List View (DRIVELIST) .PD 000 Control-k .PD This is a Win32-only feature which displays a list of all available disk drives. Details about each drive are also displayed if you have details enabled. In order for this feature to work, you must be running on Win32 AND have the \*(W3 package installed, AND the USEWIN32ALL Program Option must be True (default condition,) AND you must not have toggled these features off with the TOGWIN32ALL key described above. For more details about Drive List View, see the section below entitled, .B ADVANCED WIN32 FEATURES. .SS Selection Keys This family of commands controls the selection of one or more (or no) items in the current directory. .TP .B Select All Items (SELALL) Control-Comma Select every item in the current directory. The ".." entry at the top of the directory listing is not included. (We almost never want to include the parent directory when issuing a command on "everything in this directory". If you do wish to include the "..", do the SELALL command first, then click on ".." while holding down the Control key.) .TP .B Invert Current Selection (SELINV) Control-i Unselects everything which was selected and selects everything which was not. As with SELALL, and for the same reason, the ".." entry is never selected on an inversion. .TP .B Unselect All Items (SELNONE) Control-Period Unselect everything in the current directory. .TP .B Select Next Item (SELNEXT) Control-n Select next item down in the directory. .TP .B Select Previous Item (SELPREV) Control-p Select previous item up in the directory. .TP .B Select Last Item (SELEND) Control-e Select last item in the directory. .TP .B Select First Item (SELTOP) Control-a Select first item in the directory. This will always be the ".." entry, but it is a quick way to get to the first part of a very long directory listing which does not all fit on-screen. .TP .B Select Using Regular-Expression \'Wildcards\' (SELWILD) Control-\\ Although \*(TW provides a very rich set of keyboard and mouse selection commands, selecting a group of files out of list of hundreds or thousands in a large directory can be tedious. If the files/directories you want to select have some lexical commonality .B in their names OR details you can have \*(TW select them for you using so-called "Regular Expressions". The idea here is that you press SELWILD and enter a "matching" string or regular expression, and \*(TW will select all entries which match this criteria for you. For example, if you just enter the text, .nf tar .fi \*(TW would select every file or directory in the current display where the string "tar" .B appeared anywhere on the line for that file/directory. This is very important: Wildcard matching takes place anywhere on the visible line. So, if you have details turned on, the match can occur anywhere on the permissions, links, group, owner, and so on. Obviously, if you have details turned off, the match can only occur on the name of the file or directory since that's all that is visible. This is a purposeful design decision because it allows you to make selections on more than just the name. Say you enter the following in the SELWILD dialog: .nf drwx------ .fi \*(TW will select all directories with no permissions enabled for group or world users. The matching string above could also select other entries (not having the permissions just described), if say, this string appeared in their name ... a rather unlikely scenario, but not impossible. If we want to get .B real specific about which entries we want selected, we need to enter a "regular expression" in the SELWILD dialog. Regular expressions are a far more powerful pattern-matching tool than simple text strings and will allow you to do some fairly amazing selections. For example, this regular expression selects all entries which contain a string beginning with "Ju" followed by any other character, a single space, and ending in "0": .nf Ju. 0 .fi So, for instance, this would select files with date details (or names, or anything else on the line...) like "Jun 01", "Jul 03", and "Jul 09". No matter what you specify, a literal matching string or a regular expression, the ".." entry of the currently viewed directory is never selected by SELWILD. Notice that these regular expressions are .B not the same thing as the filename "globbing" wildcards commonly used with Unix and Win32 shells. If you enter constructs like "*.txt" or "*.tar.gz", you will not get the results you expect. In fact, these specific examples will cause \*(TW to grumble and present a warning message. For an excellent tutorial on Python-compliant regular expressions, see: .nf http://www.amk.ca/python/howto/regex/ .fi By default, SELWILD will select an entry when your regular expression matches anything on the displayed line. This allows you to make selections based on any visible column of information. This "match anywhere on the line" semantic is possible because SELWILD automatically massages the regular expression you provide to make "any match on the line" true. There may be times when you want to provide very specific regular expression definitions which seek a match at specific locations. In that case, you can prevent SELWILD from fiddling with your regular expression, by beginning it with the double-quote (") character. SELWILD understands this to mean that your regular expression is to be treated literally without modification. (It only throws away this leading escape character.) Suppose we changed our example above slightly and used this regular expression: .nf "^drwx------ .fi Now \*(TW would select .B only the directories without any group and world access because: .IP \ 10 The leading double-quote (") forces literal interpretation of the regular expression - i.e. It turns off "match anywhere" semantics as just described. The carat (^) at the beginning of the actual regular expression "anchors" the match to the start of the line. For a match to be declared (and for \*(TW to select an item) the regular expression must be satisfied at the beginning-of-line. .P .IP Because regular expressions can get complicated and tedious to type in, any such expression you use is saved in a history available via the Wildcard Menu (see below). There is also provision for pre-defining frequently used selection wildcards in your \*(CF (see below) so you don't have to type them in manually each time you start the program - you can just select them from the Wildcard Menu. .TP .B Mouse-Based Selections The mouse can also be used to select one or more items. A single-click of the left mouse button selects a particular item. Clicking and dragging selects an adjacent group of items. Clicking an item and then clicking a second item while holding down the "Shift" key also selects an adjacent group of items. Finally, a group of non-adjacent items can also be selected. The first item is selected with a single left mouse button click as usual. Each subsequent (non-adjacent) item is then selected by holding down the "Control" key when clicking on the item. .SS Scrolling Commands If a given directory's contents cannot be displayed on a single screen, \*(TW supports both vertical and horizontal scrolling via scrollbars. This capability is doubled on the keyboard with: .TP .B Scroll Page Down (PGDN) Control-v Scroll down one page in the directory listing. .TP .B Scroll Page Up (PGUP) Control-c Scroll up one page in the directory listing. .TP .B Scroll Page Right (PGRT) Control-g Scroll to the right one page width. .TP .B Scroll Page Left (PGLFT) Control-f Scroll to the left one page width. .SS Command Execution Options This family of commands causes \*(TW to actually attempt to execute some command you've chosen: .TP .B Run Arbitrary Command (RUNCMD) Control-z This is a shortcut that allows you to run any command you'd like without having to define it ahead of time in the \*(CF. It is more-or-less like having a miniature command line environment at your disposal. You may enter a number of different things in the RUNCMD dialog. You may type literal text or refer to any of the variable types (User-Defined, Environment, or Built-In) supported by \*(TW just as you do in writing Command Definitions (see below). This makes it easy to enter complex commands without having to type everything literally. For example, if you would like to copy all the currently selected files to a new directory, press RUNCMD and enter (on Unix): .nf cp [SELECTIONS] newdir .fi \*(TW understands the variable reference syntax here just as it does in a Command Definition. This also gives you a single way of referring to environment variables, regardless of OS platform. Recall that in Unix-like shells, an environment variable is in the form "$NAME", but on Win32 it is in the form "%NAME%". Instead if having to keep track of this difference, you can just use a \*(TW Environment Variable reference. For instance, assuming the EDITOR environment variable is set, this command works the same on both systems: .nf [$EDITOR] [SELECTIONS] .fi Unless you have set the MAXHIST option to 0, RUNCMD keeps track of your last manually entered command and presents it as a default when you press RUNCMD again. You can then run the command again exactly as you last entered it, you can modify it before running the command again, or you can delete it and enter an entirely new command. Commands can be edited with either the arrow and keypad keys defined on your system or by emacs editing commands like Control-a, Control-k, Control-e, and so forth. Also see the section below entitled, .B Program Option Statements, to understand the CMDSHELL option. This option greatly simplifies running command-line programs from RUNCMD so their output can been seen in a GUI window. This is particularly handy on Unix. .TP .B Run Selected File / Move To Selected Directory (SELKEY and MOUSESEL) .PD 000 Return (Enter Key) .IP DoubleClick-Left-Mouse-Button .PD If the selected item is a Directory, \*(TW will move into that directory when this command is issued. If the selected item is a file, \*(TW will attempt to execute it. Whether or not the file is actually executed depends on how the underlying operating system views that file. In the case of Unix-like operating systems, the execute permission must be set for the user running \*(TW (or their group) for the file to be executed. On Win32, the file will be executed if the user has permission to do so .B and that file is either executable or there is a Windows association defined for that file type. For example, double-clicking on a file ending with ".txt" will cause the file to be opened with the \'notepad\' program (unless the association for ".txt" has been changed). If \*(TW determines that it is running on neither a Unix-like or Win32 system, double-clicking on a file does nothing. .TP .B Run User-Defined Command User-Defined (Single Letter) Key Each command defined in the \*(CF has a Command Key associated with it. Pressing that key will cause the associated command to be run. If no command is associated with a given keystroke, nothing will happen when it is pressed. .SS Directory Shortcuts \*(TW provides a way to directly navigate into a frequently-used directory using a single keystroke. You can define up to 12 such "Directory Shortcuts" in the \*(CF. Each of the definitions is associated with one of the following 12 keys: .TP .B Navigate Directly To A Directory (KDIRSC1 ... KDIRSC12) .PD 000 F1 ... F12 .PD Pressing one of these keys changes to the directory associated with it in the \*(CF. For more information on this topic, see the discussion of the \*(CF below entitled, .B Directory Shortcut Statements. .SS Program Memories If you've used GUIs before, you're probably familiar with the idea of a program "Clipboard" - a temporary holding area which is used when cutting, copying, and pasting files. This is a good idea, but has several limitations. First, most systems only have a .B single clipboard. It would be mighty handy to have muliple Clipboard-like storage areas for keeping track of several different operations at once. Secondly, when you copy or paste something to a conventional Clipboard, .B its old contents get overwritten. There is no way to keep appending items to the Clipboard. Finally, items usually can only be cut or copied to the Clipboard .B from the current directory. It would be nice if we could not only keep adding things to the Clipboard, but be able to do so as we navigate around the filesystem. \*(TW addresses these concerns by means of 12 separate "Program Memories". As you use \*(TW, you can add (append) the names of any directories or files in the currently viewed directory by selecting them and then using the appropriate \*(TW MEMSETx key (see below). To take advantage of this feature, you write Command Definitions (or manually issue a command via the RUNCMD key) which reference the contents of a Program Memory using one of the [MEMx] Built-In Variables. (See the section below on entitled, .B Program Memory Built-Ins for more details in how to apply Program Memories). \*(TW provides key combinations for selectively setting and clearing particular Program Memories as well as a key combination for clearing all Program Memories in a single keystroke: .TP .B Clear Selected Program Memory (MEMCLR1 - MEMCLR12) Control-F1 ... Control-F12 Clear (empty) the selected Program Memory. .TP .B Clear All Program Memories (MEMCLRALL) Control-m Clear (empty) all 12 Program Memories at once. .TP .B Set Selected Program Memory (MEMSET1 - MEMSET12) Alt-F1 ... Alt-F12 Append the .B the full path names of the currently selected files and directories to the Program Memory desired. .SH MENU OPTIONS Although \*(TW is primarily keyboard-oriented, several menu-based features are also implemented to make the program more convenient to use. These menus appear at the top of the \*(TW display window, above the directory listing. A menu can be invoked in one of several ways. You can click on it, you can press its associated "Accelerator Key" combination, or you can use the "Mouse Shortcut" to cause a copy of the menu to pop-up near the mouse pointer. The Accelerator Keys are shown in parenthesis next to the menu names below and the Mouse Shortcuts are similarly shown below in square brackets. The first item in each menu is a dashed line ("----") which indicates that it is a "tearoff" menu. Clicking on the dashed line will detach the menu from \*(TW allowing it to be placed anywhere on screen. Even when detatched, these menus remain current and in-sync with \*(TW as it continues to run. You can also tear off multiple instances of these menus if you'd like copies of them at several locations on the screen simultaneously. On Win32 systems, if a menu gets too long to physically fit on screen, up- and down- scrolling arrows automatically appear at the top- and bottom of the menu respectively. This is not a feature of the Unix Tk implementation, so menus which grow too large are simply truncated to fit the screen on Unix-like systems. As described in the following paragraphs, \*(TW provides some options to limit the size of the Directory and History menus for this reason. .SS Commands Menu (Alt-c) [Right-Mouse-Button] Every command defined in the \*(CF is listed in this menu by its Command Name. The association Command Key is also shown in parenthesis. Clicking on an item in this menu is the same as invoking it from the keyboard by its Command Key. This is a convenient way to invoke an infrequently used command whose Command Key you've forgotten. It is also handy to confirm which commands are defined after you've edited and reloaded the \*(CF. The commands are listed in the order in which they are defined in the configuration file. This allows most frequently used commands to appear at the top of the menu by defining them first in the \*(CF. If no commands are defined, either because the \*(CF contains no Command Definitions or because the \*(CF cannot be opened for some reason, the Commands Menu will be disabled (grayed out). .SS Directories Menu (Alt-d) [Shift-Right-Mouse-Button] \*(TW keeps track of every directory visited. The previously described command to move "Back" one directory allows directory navigation in reverse traversal order - you can back up to where you started. However, this feature "throws away" directories as it backs up, sort of like an "undo" function. The "Directories" menu provides a slightly different approach to the same task. It keeps permanent track of every directory visited and displays that list in sorted order. This provides another way to move directly to a previously visited directory without having to manually navigate to it again, back up to it, or name it explictly using the Change Directory command. There are two user-settable options associated with the Directory Menu. MAXDIR specifies the maximum number of visited directories to .B display in the menu. This defaults to 32 as is intended as a way to keep the menu size reasonable. If you set MAXDIR=0, it means you are disabling this menu feature altogether as well as disabling the tracking of the last manually entered directory via the CHANGEDIR key. (default: Control-x) MAXDIRBUF specifies how many directories \*(TW keeps track of internally, no matter how many are actually displayed. It defaults to 250. In summary, the Directory Menu shows the last MAXDIR directories visited in alphabetically sorted order (unless you change MAXDIRBUF to be smaller than MAXDIR). "Visited", in this case, is stretching things a bit. All Directory Shortcut locations are also preloaded into the Directory Menu when the program starts even though they have not yet actually been visited. The Directory Menu is emptied and grayed out when you press the CLRHIST key. (default: Control-y) .SS History Menu (Alt-h) [Shift-Control-Right-Mouse-Button] \*(TW keeps track of every command you attempt to execute, whether it is an invocation of a Command Definition found in the \*(CF or a manually entered command via the RUNCMD key. (default: Control-z) This is done whether or not the command is successfully executed. This feature provides a quick way to re-execute a command you've previously run. When you select a command to run this way, a dialog box is opened, giving you an opportunity to edit the command before running it again. There are two user-settable options associated with the History Menu. MAXHIST specifies the maximum number of previously run commands to .B display in the menu. This defaults to 32 as is intended as a way to keep the menu size reasonable. If you set MAXHIST=0, it means you are disabling this menu feature altogether as well as disabling the tracking of the last manually entered command via the RUNCMD key. MAXHISTBUF specifies how many previously-run commands \*(TW keeps track of internally, no matter how many are actually displayed. It defaults to 250. One other point of clarification is in order here. If you run one of the commands defined in your \*(CF, it is stored in the History .B after all variable substitutions have been made. But, manually entered commands are stored in the History .B literally as typed - i.e., Without variable substitution. This allows you easily reuse a manually entered command in another directory or context. (Presumably, Command Definitions in the \*(CF are written in such a way so as to be useful across many different directories and contexts. Running such a command again is simply a matter of pressing its associated letter key once more. By storing the resolved version of the command in the History, you can see what the command actually did.) The History Menu is emptied and grayed out when you press the CLRHIST key. (default: Control-y) .SS Wildcard Menu (Alt-w) [Alt-Control-Right-Mouse-Button] \ (Note that on Win32 you must press Alt .B then Control .B then Right-Mouse-Button for this to work. Win32 appears to care deeply about keystroke order.) This menu provides a list of all previously used selection "wildcard" regular expressions. Any regular expressions defined in the \*(CF (see below) using the "WILDCARD = " statement will also appear in this menu. This saves you the tedium of constantly having to enter complex regular expression syntax every time you wish to do wildcard-based selections. Selecting something from this menu brings up a dialog box which allows you to edit the selected wildcard before using it. Bear in mind that the size of the displayed menu is governed by the MAXMENU and MAXMENUBUF \*(CF options (see below). i.e., Only the last MAXMENU number of wildcards are actually displayed on the menu. The Wildcard Menu is emptied and grayed out when you press the CLRHIST key. (default: Control-y) This history is .B not cleared if the \*(CF is reloaded. .SS Help Menu (Alt-l) [No Mouse Shortcut] This menu provides information about various internal settings of \*(TW including User-Defined Variables, Command Definitions, Internal Program Variables, User-Settable Options, Keyboard Assignments, and Directory Shortcuts. It also has an About feature which provides version and copyright information about the program. For the most part, this help information should fit on screen easily. However, very long Command Definitions will probably not fit on-screen once User-Defined and Environment Variables have been substituted. In this case, if you are curious about just how \*(TW is interpreting your Command Definitions, invoke the program with the relevant debug bit turned on and watch the output on stdout as \*(TW runs. .SH THE \*(TW CONFIGURATION FILE Much of \*(TWs flexibility comes from the fact that it is a .B macro-programmable user interface. The program itself does little more than provide a way to navigate around a filesystem. It must be configured (programmed) to actually do something with the files you specify. This is done via a "\*(CF". This file is also used to set Program Options and change keyboard assignments. Although the program will run without a \*(CF present, it will warn you that it is doing so with no commands defined. .SH LOCATION OF CONFIGURATION FILE By default, the program expects to find configuration information in .B $HOME/.twander (%HOME%\\\\.twander on Win32) but you can override this with the -c command line option. (Recommended for Win32 systems - see the section below entitled, .B INSTALLING \*(TW ) Actually, \*(TW can look in a number of places to find its \*(CF. It does this using the following scheme (in priority order): .IP \(bu 4 If the -c argument was given on the command line, use this argument for a \*(CF. .IP \(bu 4 If -c was not given on the command line, but the HOME environment variable is set, look for the a \*(CF as $HOME/.twander. .IP \(bu 4 If the HOME environment variable is not set .B and a -c command line argument was not provided, look for a file called ".twander" in the directory from which \*(TW was invoked. .SH CONFIGURATION FILE FORMAT \*(TW \*(CFs consist of freeform lines of text. Each line is considered independently - no configuration line may cross into the next line. Whitespace is ignored within a line as are blank lines. There are several possible legal lines in a \*(TW Configuration File: .nf Comments Program Option Statements Key Binding Statements Directory Shortcut Statements Wildcard Statements Variables And Command Definitions .fi (See the ".twander" file provided with the program distribution for examples of valid configuration statements.) Everything else is considered invalid. \*(TW will respond with errors or warnings as is appropriate anytime it encounters a problem in a \*(CF. An error will cause the program to terminate, but the program continues to run after a warning. For the most part, \*(TW tries to be forgiving and merely ignores invalid configuration statements (after an appropriate warning). It only declares an error when it cannot continue. This is true both when the program initially loads as well as during any subsequent \*(CF reloads initiated from the keyboard while running \*(TW. The following sections describe each of the valid Configuration File entires in more detail. .SS Comments A comment is begun with the "#" character which may be placed anywhere on a line. Comments may appear freely within a \*(CF. \*(TW strictly ignores everything from the "#" to the end of the line on which it appears without exception. This means that "#" cannot occur anywhere within a User-Defined Variable Definition, Key Binding Statement, or Command Definition (these are described below). Comments .B can be placed on the same line to the right of such statements. It is conceivable that the "#" character might be needed in the Command String portion of a Command Definition. \*(TW provides a Built-In Variable, [HASH], for exactly this purpose. See the section below entitled, .B Variables And Command Definitions, for a more complete description. .SS Program Option Statements Many of \*(TWs internal program defaults can be overriden in the \*(CF using Program Option statements. These statements look just like the User-Defined variables described later in this document except \*(TW recognizes the variable name as a Program Option rather than an arbitrary variable. Program Option Statements thus take the form: .nf Option Name = Option Value .fi The Option Name is case-sensitive and must be entered exactly as described below. For instance, \*(TW understands "AUTOREFRESH" as a Program Option, but will treat "AutoRefresh" as a User-Defined Variable. The Option Value is checked to make sure it conforms to the proper type for this variable. The Type can be Boolean, Numeric, or String. A Boolean Option must be assigned a value of True or False. These logical values can be in any case, so TRUE, TRue, and tRue all work. Note that when you view these variables in the Help menu entitled, .B User-Settable Options, they are displayed as 0 (False) and 1 (True). A Numeric Option must be a number 0 or greater. Numbers can also be entered in hexadecimal format: 0xNNN, where NNN is the numeric expression in hex. A String Option can be any string of characters but the empty string is not allowed: .nf # Bad String Option Assignment - Causes Warning BCOLOR = .fi Furthermore, as described above, you cannot use the \'#\' symbol as part of the string assignment because \*(TW always treats this character as the beginning of a comment no matter where it appears. Other than this basic type-checking, \*(TW does no further validation of the Right Hand Side of a Program Option Statement. It is perfectly possible to provide a RHS which passes \*(TWs type validation but which makes no sense whatsoever to the program. Entries like this cause everything from a mild \*(TW warning to a spectacular program failure and Python traceback on stdout: .nf # A Nice Way To Clobber \*(TW BCOLOR = goo .fi The following sections document each available Program Option using this general format: .nf Option-Name [Type] (Default Value) .fi .TP .B AUTOREFRESH [Boolean] (True) By default, \*(TW regularly re-reads the current directory to refresh the display with any changes. If you are running on a very slow machine or slow connection between the X-Windows server and client, set this option to False. You can manually force an update at any time using the REFRESH key. (default: Control-l) .TP .B BCOLOR [String] (Black) Selects the main display Background Color. .TP .B CMDSHELL [String] ("") This option is primarily intended for people running \*(TW on Unix-like operating systems like FreeBSD and Linux. As described in the .B GOTCHAS section below, running a command line program or script requires some extra effort if you want to see the results presented in a GUI window. Typically, you need to run these commands in some kind of \'xterm\' context so that the results will be visible, possibly using a shell as well. So, it's common to see Command Definitions like: .nf x MyCommand xterm -l -e bash -c 'stuff-for-my-command' .fi In fact, on Unix, the need for this idiom is so common, it's best to define some variables for this. If you look in the example \'.twander\' \*(CF provided in the program distribution, you'll see something like (comments removed): .nf SHELL = bash -c VSHELL = [XTERM] [SHELL] XTERM = xterm -fn 9x15 -l -e .fi Now the Command Definition above becomes: .nf x MyCommand [VSHELL] 'stuff-for-my-command' .fi That's all well and good for Command Definitions, but what happens when you want to .B manually enter a command via the RUNCMD key? (default: Control-z) You have to manually enter the gobbledy-gook above, or at least start your command with [VSHELL] (since RUNCMD understands variable references). The CMDSHELL option is a way to automate this. You can assign it to any literal text. That text will be .B automatically prepended to any command you enter manually. In this case you could do either of the following in the \*(CF: .nf CMDSHELL = xterm -l -e bash -c - or - CMDSHELL = [VSHELL] # Assuming VSHELL is defined previously .fi Now every time you enter a command, this will be placed in front of your text before command execution commences. To disable CMDSHELL operation .B permanently, just remove the statement above from your \*(CF. If you want to leave it in as a placeholder, but deactivate CMDSHELL, use the following statement (since the RHS of a string variable cannot be blank): .nf CMDSHELL = "" .fi You also may want to occasionally use RUNCMD to do something without CMDSHELL processing, even though that feature has been defined in the \*(CF. You can disable CMDSHELL operation on a per-RUNCMD basis. Just begin your entering your command with the backslash (\\) character. \*(TW understands this to "escape" CMDSHELL processing. As a general matter, CMDSHELL allows you to prepend .B anything you like before a manually entered command - literal text, references to variables, or even the name of a script the system will use to execute your command. Whatever value you use for CMDSHELL will appear in the Command Menu history for each manually initiated command which used this feature - i.e., All the manual commands that .B did not escape the feature. .TP .B DEBUGLEVEL [Numeric] (0) This is another way to set the debugging level you desire (the other way being the -d command line argument). For example, say you want to always dump the current Command Definitions to stdout when the program starts - perhaps you want to redirect this output to a file or printer. Just add this line to your \*(CF: .nf DEBUGLEVEL = 0x004 .fi .TP .B FCOLOR [String] (green) Selects the main display Foreground (Text) Color. .TP .B FNAME [String] (Courier) Selects the main display Font Name. .TP .B FSZ [Numeric] (12) Selects the main display Font Size. .TP .B FWT [String] (bold) Selects the main display Font Weight. This can be assigned to: normal, bold, italic, or underlined. Depending on your system, other values may also be possible. .TP .B HBCOLOR [String] (lightgreen) Selects the help menu Background Color. .TP .B HEIGHT [Numeric] (600) Initial vertical size of the \*(TW window in pixels. .TP .B HFCOLOR [String] (black) Selects the help menu Foreground (Text) Color. .TP .B HFNAME [String] (Courier) Selects the help menu Font Name. .TP .B HFSZ [Numeric] (10) Selects the help menu Font Size. .TP .B HFWT [String] (italic) This selects the help menu Font Weight. .TP .B MAXDIR [Numeric] (32) Maximum number of entries to .B display in the Directory Menu. This keeps the menu size reasonable. Internally, \*(TW keeps track of way more than this number of directories (see the MAXDIRBUF option below). .TP .B MAXDIRBUF [Numeric] (250) Maximum number of visited directories \*(TW .B tracks internally. This value need normally not be changed. It is present only to bound how much memory \*(TW consumes for this task. .TP .B MAXHIST [Numeric] (32) Maximum number of entries to .B display in the History Menu. This keeps the menu size reasonable. Internally, \*(TW keeps track of way more than this number of commands (see the MAXHISTBUF option below). .TP .B MAXHISTBUF [Numeric] (250) Maximum number of commands executed \*(TW .B tracks internally. This value need normally not be changed. It is present only to bound how much memory \*(TW consumes for this task. .TP .B MAXNESTING [Numeric] (32) Number of times a Command Definition is processed to dereference all variables. For example, suppose you have this: .nf FOO = bax BAM = x[FOO] x mycmd [BAM] [SELECTION] .fi When you press the x key, the \*(TW command interpreter has to process the line repeatedly until all variables are resolved: .nf [BAM] [SELECTION] -> x[FOO] [SELECTION] x[FOO] [SELECTION] -> xbax [SELECTION] xbax [SELECTION] -> xbax selected-item .fi So, in this case, it took 3 iterations to do this. MAXNESTING merely sets the maximum number of times this is permitted. We have to do this to stop runaway definitions like this: .nf FOO = x[FOO] .fi This kind of construct will cause \*(TW to iterate MAXNESTING number of times and then give up with a warning about exeeding the nesting (dereferencing) limit. A 32 iteration limit should be plenty for any reasonable Command Definitions. If you set MAXNESTING to 0, \*(TW will not allow .B any variable dereferencing, .B including the Built-In Variables. This is probably not what you want. .TP .B MBARCOL [String] (beige) Selects the Menu Bar color. .TP .B MBCOLOR [String] (beige) Selects the menu Background Color. .TP .B MFCOLOR [String] (black) Selects the menu Foreground (text) Color. .TP .B MFNAME [String] (Courier) Selects the menu Font Name. .TP .B MFSZ [Numeric] (12) Selects the menu Font Size. .TP .B MFWT [String] (bold) Selects the menu Font Weight. .TP .B NODETAILS [Boolean] (False) Prevents details from ever being displayed. .TP .B NONAVIGATE [Boolean] (False) Prevents the user from navigating out of the starting directory. Command Definitions and commands initiated manually via RUNCMD (default: Control-z) can still "see" other directories, the user just cannot move elsewhere with any of the \*(TW navigation commands. The NODETAILS and NONAVIGATE commands are .B not security features. They can easily be defeated by editing the \*(CF. They exist to make it easy for you to create \'twander' configurations for technically unsophisticated users. Say you want to define a few simple commands for your boss to use which won't challenge his or her feeble managerial mind ;) By defining these commands and setting both NODETAILS and NONAVIGATE to TRUE, you really limit what can be done with \*(TW. They can't wander off into other directories and get lost, or worse yet, clobber files they don't understand. There are no details to confuse them. Your instructions for using the program thus become, "Select the files you're interested in and press P to print them, M to mail them to Headquarters.." and so on. Again, .B these are NOT security features. Anyone with even very modest technical skills can thwart these limitations. But, it is suprising just how effective these can be in simplifying life for technically challenged users. .TP .B QUOTECHAR [String] (") As described below, \*(TW ordinarily quotes most Built-In Variables as it replaces them during command processing. This is useful because modern operating systems allow file and directory names to have spaces in them. Such names must be quoted for most programs to understand them as a single entity on a command line. By default, the double-quote char is used for this purpose. You can suppress quote processing by using the -t command line argument. This does nothing more than set QUOTECHAR to an empty string. Unfortunately, since the RHS of a Program Option Statement cannot be blank, you cannot disable quoting with this option. However, you .B can set the quotation character to be anything else you like, such as a single-quote. In fact, you can set QUOTECHAR to any .B string of characters you like and they will faithfully be used on either side of a Built-In Variable replacement. .TP .B REFRESHINT [Numeric] (3000) Nominal time in milliseconds between automatic directory refreshes (if AUTOREFRESH is True). This time is .B really nominal and should not be used with any accurate timing in mind. REFRESHINT=5000 says that the refresh interval will be nominally 5 seconds (and certainly more than the default of 3 seconds), but it can be off this nominal value by quite a bit. If you run \*(TW on a slow system (or have a slow link between X-Client and X-Server) you might want to increase this value substantially. You can get into the situtation where just as one refresh completes, its time to do the next one, and the \*(TW will seem really sluggish and unresponsive. By lengthening the time between automatic updates, the amount of unresponsive behavior is reduced. Of course, this also means that any changes in the currently viewed directory will also take longer to appear in the \*(TW display. .TP .B STARTDIR [String] (Directory In Which Program Started) This allows you to force a starting directory of your choice no matter where the program actually is launched. This is useful for day-to-day operation - perhaps you always want to start in your home directory. STARTDIR is also handy in tandem with the NODETAILS and NONAVIGATE options to force a user to the only directory which they should be using. .TP .B STARTX [Numeric] (0) Initial horizontal offset of the \*(TW window in pixels. .TP .B STARTY [Numeric] (0) Initial vertical offset of the \*(TW window in pixels. .TP .B USETHREADS [Boolean] (True) Normally, \*(TW uses threads to run the commands you've defined/entered and requested. As noted later in the .B GOTCHAS section, there are circumstances under which this does not work properly. In this case setting USETHREADS=False, will cause \*(TW to use more traditional process mechanics to run a command. This option applies only to Unix-like operating systems. Win32 commands are .B always run as a thread - this is the only process model Win32 supports.. .TP .B USEWIN32ALL [Boolean] (True) Win32 only. If \*(W3 is installed, determines whether its features should be used (see section below entitled, .B ADVANCED WIN32 FEATURES for details). Normally, this option should be left alone. However, if you have \*(W3 installed on your system for some other reason, but don't want it used by \*(TW, set this option to False. The main reason to do this would be on a slow machine with very large directories. The advanced features of \*(W3 come at a computational price. This is especially noticeable when it is computing the attributes, ownership, and size in a directory with hundreds (or more) of entries. Typically, you would just use the TOGWIN32ALL key (default: Control-w) to temporarily disable these features before entering such a directory. However, if your starting directory is in this category, setting USEWIN32ALL=False might not be a bad idea. .TP .B WARN [Boolean] (True) Determines whether interactive warnings should be displayed as \*(TW encounters them (while parsing a Configuration File or just in normal execution). Setting this option to False is the same thing as using the -q command line option with one important difference: The \*(CF is parsed before the command line is parsed. Even if you have -q on the command line (or in the TWANDER environment variable), if there is an error in your \*(CF, you will see warning messages at program startup time. Putting WARN=False at the top of your \*(CF will suppress this. It is not recommended that you operate normally with the -q flag or with WARN=False. \*(TW is pretty forgiving in most cases and when it does warn you about something, there is a good reason for it - you probably want to know what the problem is. .TP .B WIDTH [Numeric] (800) Initial horizontal size of the \*(TW window in pixels. .P A few general notes about Program Options are worth mentioning here: .IP \(bu 4 You can set the same option multiple times in a single \*(CF - \*(TW pays no attention. However, only the .B last (the one nearest the end of the file) instance of that Program Option Statement actually takes effect. This is handy if you want to temporarily change something without modifying your existing configuration. Just add your temporary change at the end of the file. When you're done with it, just remove it. No need to edit and re-edit your preferred configuration... .IP \(bu 4 The font colors, weights, and sizes available for your use will vary somewhat by system. For instance, Win32 TrueType fonts are effectively available in every size and weight. On the other hand, most Unix-like systems have a more limited palette of fonts and colors with which to work. Most systems should support obvious color names like, red, white, blue, yellow, beige, and so on. Many also support colors like lightgreen, lightblue, etc. At a minimum, you should be able to use normal, bold, italic, and underline for font weights. Most systems attempt some kind of "best fit" font matching. If you specify a font size/weight/name that does not exist, the system will try to find what it thinks is the closest match. This is usually ugly, so try to specify font information for things that actually exist on your system. If your setting in the \*(CF seems not to work, take a look at the command window in which you started \*(TW (or start it from one manually, if you're using a GUI shortcut to start it). Attempts to use unavailable colors and weights will cause Python/Tkinter to dump traceback information on stdout. .IP \(bu 4 Although you can use proportionally spaced fonts with \*(TW, the result is pretty ugly. \*(TW assumes a fixed width font when it calculates display formatting. Variable-width fonts will cause your display to be ragged and hard to read. .IP \(bu 4 If you set MAXDIR or MAXDIRBUF to 0, it disables both the tracking of visited directories and the retention of last manually entered directory with CHANGEDIR. (default: Control-x) .IP \(bu 4 Similarly if you set MAXHIST or MAXHISTBUF to 0, you disable both command history and the retention of the last manually entered command via RUNCMD. (default: Control-z) .IP \(bu 4 Changing either MAXDIR or MAXHIST and then reloading the Configuration File only changes .B the number of items visible on their respective menus. \*(TW actually keeps track of more than this internally (governed by the MAXDIRBUF and MAXHISTBUF options). Say MAXDIR and MAXHIST are set to 4, but you've actually visited 20 different directories and issued 30 commands. You'll only see 4 of each. But, if you edit MAXDIR and MAXHIST to now be 32 and reload the \*(CF, you will see all 20 directories and 30 commands on their respective menus. .IP \(bu 4 At first glance, the ability to set QUOTECHAR to any arbitary string may seem silly, but it actually has a purpose. As good as the \*(TW macro capability is, it is still a fairly simple language. Really complex tasks will need to be handed off to some other scripting language (like Python!). It may be useful to delimit Built-In Variables (which indicate your selections via the \*(TW interface) in such a way that your script knows where they came from. So, say you set QUOTECHAR=+++ and you have a Command Definition like this: .nf x mycmd MyPythonScript [DSELECTIONS] other stuff .fi When MyPythonScript runs, it can immediately tell which arguments came from \*(TW (the ones that are in the form +++dir+++ or +++file+++) and which arguments are just other stuff. You probably won't need this often, but its nice to have. .IP \(bu 4 STARTX and STARTY are relative to the (0,0) origin that Tk uses for window placement. In High-School algebra most of us got used to seeing (0,0) in the lower-left corner of a graph. Tk has a rather different view of this and STARTX and STARTY are relative to the .B upper-left corner of the screen. .SS Key Binding Statements No program that runs in many operating environments can satisfy everyone's (anyone's!) idea of what the "correct" key bindings should be. An emacs user, vi user, BSD user, and Windows user are going to differ considerably on what keys should be bound to what feature. \*(TW ships from the factory with a set of default key bindings, but it also provides a mechanism for changing these bindings via entries in the \*(CF. This feature is available only for .B Keyboard Assignments. Mouse Button Assignments may not be changed by the user. An attempt to do so in the \*(CF will cause \*(TW to display a warning and ignore the offending line. It is not difficult to override the default keyboard bindings by adding entries in the \*(CF. Doing so requires some familiarity with how Tkinter names keystrokes. Good resources for learning this exist abundantly on the Internet, among them: .nf http://www.pythonware.com/library/tkinter/introduction/index.htm http://www.nmt.edu/tcc/help/pubs/lang.html http://www.cs.mcgill.ca/~hv/classes/MS/TkinterPres/ .fi (As an aside - Tkinter is nothing more than a Python interface to the Tcl/Tk windowing system. The "real" naming conventions for keystokes can be found in the many sources of Tk documentation, both in print and on the Internet.) Keyboard binding assignments look just like variable definitions in the \*(CF. (The \*(TW \*(CF parser automatically distinguishes between Key Binding Statements and Variable Definitions or other legitimate statements. This means you can never use one of the program function names as one of your own variable names.) Key Binding Statements thus take the form: .nf Program Function Name = Tkinter Keystroke Name .fi Changing the default bindings is therefore nothing more than a matter of assigning the appropriate Program Function Name (found in parenthesis next to the description in the default descriptions above) to the desired keystroke. Examples of all the default key bindings are shown as comments in the ".twander" example \*(CF supplied in the program distribution. The easiest way to rebind a particular function is to copy the relevant line, uncomment the copy, and change the right side of the assignment to the new key you'd like to use. It is important to observe several rules when rebinding keys: .IP \(bu 4 It is best if keyboard navigation commands are all Control or Function keys. If you assign a navigation or selection function to a single keystroke, it may conflict with a user-defined command. If you assign it to a keypad/special key it may conflict with that key's normal GUI behavior. .IP \(bu 4 The Tkinter keynames should placed on the right side of the "=" symbol .B without any quotation marks. .nf # Incorrect QUITPROG = '<F3>' # Correct QUITPROG = <F3> .fi .IP \(bu 4 The Program Function Name variables (the left side of the assignment) may not be used as names for your own user-defined variables elsewhere in the \*(CF. In fact, \*(TW will never even recognize such an attempt. For example, suppose you try to do this: .nf QUITPROG = something-or-other .fi Because you want to be able to reference [QUITPROG] in a subsequent Command Definition. \*(TW will actually interpret this as just another key binding command, in this case binding the program function QUITPROG to "something-or-other" - probably not what you intended. Moreover, if you have a Command String somewhere with [QUITPROG] in it, \*(TW will declare and error and abort because it has no User-Defined variable of that name in its symbol table. .IP \(bu 4 When you're done making changes to the \*(CF, be sure to either restart the program or reload the \*(CF to assign the new bindings. .IP \(bu 4 Be aware that \*(TW does no sanity testing on the assignments you change. If you assign a particular \*(TW function to an illegal or silly key string, the program will probably blow-up spectacularly. At the very least, that program feature will probably be unusable, even if \*(TW manages to run. .SS Directory Shortcut Statements \*(TW provides a mechanism for directly navigating into one of 12 frequently used directories. 12 keys, KDIRSC1 ... KDIRSC12 (default: F1 ... F12) have been set aside for this purpose. Directory Shortcut Statements are entries in the \*(CF which associate one of these keys with a particular directory path. These statements are in the form: .nf DIRSCxx = path where, xx is a number from 1-12 .fi So, for example, if you want to enter "C:\\Documents And Settings" when you press the F5 key, you would add this to your Configuration File: .nf DIRSC5 = c:\\Documents And Settings .fi There are several subtleties to Directory Shortcuts you should understand: .IP \(bu 4 You can end the path with slash or not - \*(TW will understand the entry either way. .IP \(bu 4 If there is no path on the righthand side of a Directory Shortcut Statement, this is the same as having no definition at all for that key: .nf # This "undefines" shortcut #5 DIRSC5 = .fi .IP \(bu 4 A given Directory Shortcut can only be defined once. An attempt to redefine it will cause a warning message to appear, and the second instance of the definition to be ignored. .IP \(bu 4 All defined Directory Shortcut paths are also automatically inserted into the Directory Menu at program startup (or \*(CF reload) whether or not you've actually visited those directories. The assumption is that you will be visiting these directories a lot (which is why you've defined shortcuts to them), so \*(TW also makes them available in the directory "history" for easy access. .IP \(bu 4 \*(TW does absolutely no checking of what you enter to the right of the equals sign. If you enter something silly for the shortcut path, you will probably get a warning that the directory cannot be opened when you try to run that shortcut. .IP \(bu 4 Keep the Program Function Names .B (KDIRSC1 ... KDIRSC12) which are used for Key Binding, distinct in your thinking from the Directory Shortcut Names .B (DIRSC1 ... DIRSC12) which are used for defining the shortcuts. .IP \(bu 4 If you enter a Directory Shortcut Name that is invalid or out of range - examples include, DIRSC01 and DIRSC13 - \*(TW treats them like a User-Defined Variable as described below. .SS Variables And Command Definitions Most programs "ship from the factory" with a pre-defined set of features or commands. \*(TW comes with .B no built-in commands! Instead, it comes with a mechanism which allows you to specify your own .B Command Definitions. By means of a simple and very powerful macro lanuage, you "program" \*(TW and equip it with commands of your own choosing. For example, you might define commands to copy, delete, edit, and move the files or directories you choose. Perhaps you have a specialized shell script for doing backups. It's a simple matter to write a \*(TW Command Definition that will pass the names of the files and directories you've selected to that backup script. You might combine this with \*(TWs Program Memory feature to keep a running list of the files and directories you want to backup and then finally issue the backup command when you're ready. Best of all, commands you define this way are always a single keystroke. This means that once you've programmed \*(TW to suit your needs, actually using it is very fast and convenient. Command Definitions are built out of literal text and may also have any combination of three variable types: User-Defined Variables, Environment Variables, and Built-In Variables. .SS Wildcard Statements As discussed above, \*(TW provides powerful regular expression-based "wildcard" selection capabilities via the SELWILD command. (default: Control-\\) These regular expressions can be complex and tedious to enter by hand each time you need them. You can pre-defined frequently needed wildcard strings in your Configuration File using the following statement: .nf WILDCARD = regular-expression-string .fi This regular expression will then be pre-loaded into the Wildcard Menu (making it easy to invoke by just clicking on it) when \*(TW starts. You may place as many of these as you like in your \*(CF. (Though the menu will be limited to displaying MAXMENU number of items - see the section above on Program Option Statements.) .SS User-Defined Variables And Environment Variables User-Defined Variables are defined using the syntax: .nf Variable Name = Replacement String .fi Environment Variables are referenced using the syntax: .nf [$VARIABLE] .fi Say we have a configuration line like this, .nf EDITOR = emacs blah blah blah blah .fi Later on, when defining a command, instead of typing in "emacs blah blah blah blah", you can just refer to the variable [EDITOR] - the brackets indicate you are .B referring to a previously defined variable. Similarly, suppose you have an environment variable called "EDITOR" which indicates your preferred editing program. Our definition could thus become: .nf EDITOR = [$EDITOR] blah blah blah blah .fi Why bother with this? Because it makes maintaining complex \*(CFs easier. If you look in the example ".twander" \*(CF provided in the program distribution, you will see this is mighty handy when setting up complex "xterm" sessions, for example. Here are several other subtleties regarding User-Defined Variables: .IP \(bu 4 \*(TW variable definitions are nothing more than a string substitution mechanism. Suppose you have a variable definition that refers to another variable: .nf NewVar = somestring [OldVar] .fi It is important to realize that this only means: "If you encounter the string \'[NewVar]\' .B in a subsequent Command Definition, replace it with the string \'somestring [OldVar]\'. In other words, no evaluation of the right side of the expression takes place when a variable is .B defined. Evaluation of a variable only takes place when the variable is .B referenced (in the Command String portion of a Command Definition). The Command Definition parser will continue to dereference variable names until they are all resolved or it has reached the maximum nesting level (see next bullet). .IP \(bu 4 User-Defined Variables may be .B nested up to 32 levels deep (this default can be changed via the MAXNESTING Program Option). You can have constructs like: .nf Var1 = Foo Var2 = Bar FB = [Var1][Var2] .fi Later on (when defining some command) when \*(TW runs into the variable reference [FB], it will keep substituting variables until all [...] references have been resolved or it hits the nesting limit (The default is 32, but you can change it with the MAXNESTING option). This limit has to be imposed to catch silly things like this: .nf Var = a[Var] .fi This recursive definition is a no-no and will be cause \*(TW to generate an error while parsing the \*(CF and then terminate. Your variable definitions can also nest other kinds of variables (Environment and Built-Ins). So, constructs like this are perfectly OK: .nf Var1 = [$PAGER] Var2 = command-arguments V = [Var1] [Var2] [DSELECTION] .fi .IP \(bu 4 In the example above, notice that since the right-hand side of User-Defined Variables is literally replaced, we have to make sure there is space between the various variable references. If we used [Var1][Var2][DSELECTION] we would get one long string back instead of a command with arguments and a list of selected items. .IP \(bu 4 Variables must be .B defined before they are referenced (in a Command Definition). You can, however, include not-yet defined variable name in another User-Variable Definition so long as all these variable are defined by the time they appear in a Command String. The following is OK because all variables are defined by the time they are actually needed: .nf Var1 = foo Var2 = [Var3] # This is just a string substitution, not a reference Var3 = bar MyVar = [Var1][Var2] # Now comes the Command Definition # If we put this before the Variable Definitions above, # it would be an error. x mycommand [MyVar] .fi .IP \(bu 4 Variable Names are case-sensitive - [EDITOR], [Editor], and [editor] all refer to different variables. .IP \(bu 4 The "#" character cannot be used in either the variable name or the replacement string since doing so begins a comment. .IP \(bu 4 The "=" is what separates the Variable Name from the replacement string. Therefore, the "=" cannot ever be part of a Variable Name. A Variable Name cannot begin with "$" (see next bullet). Other than these minor restrictions, both Variable Names and Replacement Characters can be any string of characters of any length. Good judgment would suggest that Variable Names should be somewhat self-descriptive and of reasonable length - i.e., Much shorter than the replacement string! .IP \(bu 4 A Variable Name must never begin with "$". This is because a Command Definition containing a string in the form [$something] is understood by \*(TW to be a reference to an .B Environment Variable, named "something". If you do this: .nf $MYVAR = some-string .fi You will never be able to subsequently reference it because, [$MYVAR] tells \*(TW to look in the current environment, not its own symbol table to resolve the reference. However, note that "$" symbol may appear anywhere else but the first character of a variable name. So, for example, MY$VAR is fine. .IP \(bu 4 Variable Names may not be redefined. This means you can only define a given Variable Name once per \*(CF. It is also considered a variable redefinition if you try to use a variable name which matches either one of the Built-In Variables (used in Command Definitions) or one of the Program Function Names (used for Key Bindings). .SS Command Definitions The heart of the \*(TW configuration process is creating of one or more .B Command Definitions. These definitions are the way user-defined commands are added to a given instance of \*(TW. A Command Definition consists of three fields separated by whitespace: .nf Command-Key Command-Name Command-String .fi The .B Command Key is any single character which can be typed on the keyboard. This is the key that will be used to invoke the command from the keyboard. Command Keys are case-sensitive. If "m" is used as a Command Key, "M" will not invoke that command. Command Keys must be unique within a given \*(CF. If \*(TW finds multiple Command Definitions assigned to the same Command Key, it will warn you and ignore everything except the first definition. A Command Key can never be "#" which is always understood to be the beginning of a comment. The .B Command Name is a string of any length containing any characters. This is the name of the command which is used to invoke the command from the Command Menu. Command Names are case-sensitive ("command" and "Command" are different names), but they are not required to be unique within a given \*(CF. That is, two different Command Definitions may have identical Command Names associated with them, though this is not ordinarily recommended. The .B Command String is any arbitrary string which is what \*(TW actually tries to execute when the command is invoked. .SS A Simple Command Definition In its simplest form, a Command Definition looks like this: .nf # A simple Command Definition m MyMore more somefile .fi This command can be invoked pressing the "m" key on the keyboard or selecting the "MyMore" entry from the Command Menu - either directly from the menu or from the Command Menu Pop-Up. No matter how it is invoked, \*(TW will then execute the command, "more somefile". The problem is that this command as written actually will not give you the result you'd like (...well, on X-Windows - is does work on Win32 as written). (For more details on why, see the .B GOTCHAS section below.) It turns out that starting a non-GUI program like \'more\' in a new window needs some extra work. What we want to do is run \'more\' inside a copy of \'xterm\'. Now our command looks like this: .nf # Our command setup to run as a GUI window m MyMore xterm -l -e more somefile .fi .SS User-Defined Variables In A Command String The last example works quite nicely. But, we're probably going to end up using the string "xterm -l -e" over and over again for any shell commands we'd like to see run in a new window. Why not create a User-Defined Variable for this string so we can simplify its use throughout the whole \*(CF? Now, our command looks like this: .nf # Our command enhanced with a User-Defined Variable. # Remember that the variable has to be defined *before* # it is referenced. XTERM = xterm -l -e # This defines the variable m MyMore [XTERM] more somefile # And the command then uses it .fi .SS Environment Variables In A Command String This is all very nice, but we'd really like a command to be generic and be easily used by a variety of users. Not everyone likes the "more" program as a pager. In fact, on Unix-like systems there is an environment variable ($PAGER) set by each user which names the paging program that user prefers. We can refer to environment variables just like any other variable as explained previously. Now our command looks like this: .nf # Our command using both a User-Defined Variable and # an Environment Variable to make it more general XTERM = xterm -l -e m MyMore [XTERM] [$PAGER] somefile .fi .SS Built-In Variables In A Command String It would also be really nice if the command applied to more than just a single file called "somefile". The whole point of \*(TW is to allow you to use the GUI to select one or more directories and/or files and have your Command Definitions make use of those selections. \*(TW uses a set of .B Built-In Variables to communicate the current directory and user selections to the any commands you've defined. Built-In Variables are referenced just like User-Defined Variables and Environment Variables and may be inserted any appropriate place in the Command String. In our example, we probably want the command to pickup whatever item the user has selected via the GUI and examine that item with our paging program. Now our command becomes: .nf # Our command in its most generic form using # User-Defined, Environment, and Built-In Variables XTERM = xterm -l -e m MyMore [XTERM] [$PAGER] [DSELECTION] .fi The "DSELECTION" built-in is what communicates the currently selected item from the GUI to your command when the command actually gets run. .SS Selection-Related Built-Ins \*(TW has a rich set of Built-In Variables for use in your Command Definitions. The first group of these is used to convey your current directory and items which you've selected to a Command Definition: .IP \(bu 4 .B [DIR] [DIR] is replaced with the current directory \*(TW is viewing. .IP \(bu 4 .B [DSELECTION] [DSELECTION] is replaced with the full path name of the item currently selected in the GUI. If more than one item is selected, [DSELECTION] refers to the last item in the group (the bottom-most, not the most recent item you selected). .IP \(bu 4 .B [DSELECTIONS] [DSELECTIONS] is replaced with the full path name of .B all items currently selected in the GUI. .IP \(bu 4 .B [SELECTION] [SELECTION] is replaced with the name of the currently selected item in the GUI. The path to that file is .B not included. As with [DSELECTION], if more than one item is selected in the GUI, the name of the last item in the group is returned for this variable. .IP \(bu 4 .B [SELECTIONS] [SELECTIONS] is replaced with the names of .B all items currently selected in the GUI. The path to those names is not included. .SS Prompting And Special-Purpose Built-Ins There are also several special-purpose Built-In Variables which are used for creating more powerful Command Definitions: .IP \(bu 4 .B [HASH] Because \*(TW always recognizes the "#" as the beginning of a comment, there is no direct way to include this character in a Command String. It is conceivable that some commands (such as \'sed\') need to make use of this character. The [HASH] built-in is provided for this purpose. Anywhere it appears in the Command String, it will be replaced with the "#" at command execution time. Unlike all the other Built-In Variables, [HASH] is never quoted when it is replaced in a Command String (regardless of whether the -t command argument is used or how the QUOTECHAR Program Option is defined). .IP \(bu 4 .B [PROMPT:Prompt-String] [PROMPT:...] allows you to insert an interactive prompt for the user anywhere you'd like in a Command String. The user is prompted with the "Prompt String" and this variable is replaced with their response. If they respond with nothing, it is interpreted as an abort, and the command execution is terminated. This makes commands extremely powerful. For instance, say you want to create a group copy command: .nf # Copy a group of items to a location set by # the user at runtime UnixCopy = cp -R Win32Copy = copy # Unix Version c UnixCP [UnixCopy] [DSELECTIONS] [PROMPT:Enter Destination] # Win32 Version C Win32CP [Win32Copy] [DSELECTIONS] [PROMPT:Enter Destination] .fi .IP \(bu 4 .B [YESNO:Question-String] [YESNO:...] allows you to prompt the user with a dialog containing a Yes/No question and buttons for their response. If the user presses "Yes", command interpretation/execution continues. If the user presses "No", the command is aborted. This is handy when you want to make sure the user really wants to run the command before continuing. For instance, suppose you define a recursive file/directory deletion command. Before running it, it's good to prompt the user to confirm their intentions: .nf D BigDelete [YESNO:Are You Absolutely Sure About This?] rm -rf [SELECTIONS] .fi .SS Program Memory Built-Ins As described previously, \*(TW implements an advanced notion of a Clipboard called "Program Memories". There is a corresponding group of Built-In Variables which allows the contents of these memories to be used in a Command Definition: .IP \(bu 4 .B [MEM1] ... [MEM12] Return the file/directory names currently stored in the indicated memory. For example, to move all the files/directories currently named in the first Program Memory to the current directory we could define a move command like this: .nf m move mv [MEM1] ./ .fi .SS Notes On Built-In Variable Use .IP \(bu 4 Built-In Variables which return a directory name do .B NOT append a path separator character ("/" or "\\") to the end of the name even though it is visible in the GUI. This provides maximum flexibility when defining commands. It is up to the command author to insert the appropriate path separator character where needed. (NOTE: Earlier releases of \*(TW .B did include the trailing path separator and you may have to edit older \*(CFs accordingly. This change was necessary because certain commands like Unix \'cp\' will not work if given a source directory with the path separator included.) For example, another way to express the full path of the currently selected item is: .nf # Unix Path Separator UPSEP = / #Win32 Path Separator WPSEP = \\ [DIR][UPSEP][SELECTION] - or - [DIR][WPSEP][SELECTION] .fi Be aware that, because of \*(TW quoting rules, such constructs will result in strings like: .nf "/mydir"/"myfile" - or - .B "C:\\mydir"\\"myfile" .fi This should not generally be a problem with the various Unix shells, and may work for some Win32 commands. However, some Win32 programs (noted in \'notepad\') reject this kind of file name when passed on the command line. The workaround (and a generally easier way to do this sort of thing), is to use the [DSELECTION] built-in which returns the full path name of an item as a single quoted string. .IP \(bu 4 User-Defined and Environment Variables are processed at the time the \*(CF is read by \*(TW. That is, they are handled .B once at load time. .IP \(bu 4 By contrast, Built-In Variables are resolved .B on each command invocation, i.e - at command runtime. .IP \(bu 4 The results of all built-ins (except HASH) are put inside double-quotes when they are replaced in the Command String. This default is recommended so that any built-in substitutions of, say, file names with spaces in them, will be properly recognized by your commands. You can suppress the addition of double-quotes by using the -t command line option when starting \*(TW. .IP \(bu 4 Any of the variable types may appear multiple times in the same Command String. For example, suppose you want to define a generic Unix copy command: .nf g gencopy cp -R [PROMPT:Enter Source] [PROMPT:Enter Destination] .fi When the user presses "g" (or clicks on "gencopy" on the Command Menu), they will be presented with two prompts, one after the other, and then the command will run. .SH ADVANCED WIN32 FEATURES As shipped from the factory, \*(TW runs pretty much identically on various Unix variants (FreeBSD, Linux) and Win32. However, \*(TW is written to take advantage of Mark Hammond's \*(W3 Python extensions if they are present on the system. These extensions add many Windows-specific features to Python and allow \*(TW to provide quite a bit more Windows-centric information about files, directories, and drives. You do .B not have to install \*(W3 for \*(TW to operate properly on your Win32 system. Installing this package just means you'll get even more \*(TW features on Win32 than you would otherwise. If you've installed \*(W3, you can toggle these features on- and off with the TOGWIN32ALL key described above. .SS Getting \*(W3 You can get the \*(W3 extensions one of two ways. If you've installed the Active State version of Python for Win32, (http://www.activestate.com/Products/ActivePython/) \*(W3 is already installed on your system. If you installed the standard Python release for Win32 (http://www.python.org/download/download_windows.html), you must add \*(W3 to your installation. You'll find the extensions and painless installation instructions at: http://starship.python.net/crew/mhammond/ .SS New Features Supported With \*(W3 One important note is in order here: The features enabled by \*(W3 are only available on "true" Win32 systems like Windows 2000 and Windows XP. Earlier versions of Windows like Win98 and WinME emulate portions of the Win32 API and do not implement the advanced security features found in the NTFS file system. Therefore, as noted below, some of these features will not work on any of the older 16-bit Windows operating systems. \*(TW handles this gracefully without blowing-up so you can safely have \*(W3 installed on one of these older systems to take advantage of the features that do work. Once you have these extensions installed, \*(TW will automatically enable three new features otherwise unavailable. .IP \(bu 4 When viewing file/directory detail information, the owner and group names will be the actual names reported by the operating system rather than the filler values normally seen in those fields (\'win32owner\' and \'win32group\'). (Does not work on older Windows systems like Win98.) .IP \(bu 4 Instead of showing Unix-style file permissions (which don't mean much under Win32), systems with \*(W3 installed will show the so-called "file attributes" maintained by the operating system. Each detailed entry in the display will have one or more of the following attributes displayed in what is normally the Unix permissions field: .nf d - Directory A - Archive C - Compressed H - Hidden N - Normal R - Read-Only S - System .fi .IP \(bu 4 A top-level "Drive List View" is enabled if \*(W3 is installed. This shows you a list of all currently available drives reachable by the system, and information about those drives. For locally attached drives, the drive label is shown. For network-attached drives, the share string is shown. The drive type (CD/DVD, Fixed, Ramdisk, Remote, Removable) is shown as are the free/total space statistics. As is the case with other \*(TW displays, these details can be toggled on- and off via the TOGDETAIL key. You can enter the Drive List View in a number of ways: .nf 1) Select the ".." from the root directory of any drive. 2) Enter the string "\\\\" from the CHANGDIR dialog. 3) Press the DRIVELIST key. (default: Control-k) 4) Start \*(TW using "\\\\" as the starting directory argument, either on the command line or using the \*(CF STARTDIR option. .fi The "Drive List View" is available on all Win32 variants, however the free/total space values will be incorrect on older systems like Win98. .SS Notes On Drive List View The Drive List View is a little different than the usual file/directory view. Program behavior (semantics) is thus also slightly different than usual in several ways: .IP \(bu 4 While in Drive List View, the various Built-In Variables which return the current selections will return .B the name or names of the selected drive(s) (without a trailing slash) just as you would expect them to in a normal file/directory view. This allows you to write commands which take drive names (letters) as an argument. The [DIR] Built-In returns an empty string in this view. .IP \(bu 4 Normally, as you navigate around a file system, \*(TW sets its own program context to the current directory. This is why you can write Command Definitions using only the file/directory name currently selected - \*(TW knows the current directory. When you are in Drive List View, the notion of "current directory" has no real meaning. So, \*(TW treats the directory from which you entered Drive List View as the "current directory" while in that view. .IP \(bu 4 By default, \*(TW automatically rereads the current view about every 3 seconds. This is fine for a file/directory view but would be annoyingly slow in the Drive List View since it takes a moment or two to get the status of any floppy disk drives attached to the system. Instead of forcing the user to listen to (and wait for) the floppy drive status to be determined every 3 seconds, \*(TW .B only reads the drive information once when it enters Drive List View. This means if a drive is connected or a floppy is inserted into the system while in Drive List View, this fact will not be automatically noted. You can force a manual update of the Drive List View by pressing the REFRESH key. (default: Control-l) .IP \(bu 4 The TOGWIN32ALL key (default: Control-w) is disabled in Drive List View. Drive List View is only available in \*(W3 mode and toggling that mode off makes no sense here. .IP \(bu 4 The SELALL (default: Control-comma) and SELINV (default: Control-i) features work slightly differently in Drive List View than they do otherwise. Ordinarily, these features never select the first item of a file/directory display because it is always the ".." entry pointing to the directory parent. In Drive List View, the first entry .B is an entry of interest - usually, but not always, Drive A: - so these two keys .B do select it as is appropriate. .SS Disabling \*(W3 Features You can toggle these features on-and off using the TOGWIN32ALL key. (default: Control-w) You can also permanently disable them by setting the USEWIN32ALL option to False in the \*(CF. This allows you to leave \*(W3 installed on your system if you need it for other reasons but don't want these features enabled in \*(TW .SH GOTCHAS There are several tricky corners of \*(TW which need further explanation: .SS Program Starts Very Slowly \*(TW attempts to determine the name of the host on which it is running at program startup. This is used in the title bar display. It first looks to see if the environment variable HOSTNAME is set, and uses that value if it is. If this variable is not set, \*(TW does a socket call to see if it can determine the hostname that way. Either of these methods works fine, but the socket call can be very slow if the network is misconfigured or malfunctioning. If \*(TW is starting very slowly, try setting HOSTNAME explicitly in your environment - this will prevent the socket call from ever taking place. A simple way to do this with \'ksh\' or \'bash\' is: .nf export HOSTNAME=`hostname` .fi (Note the backticks used to execute the \'hostname\' program and assign its results to HOSTNAME.) Be aware that \'bash\' claims to automatically set this variable when it starts. However, it does not appear to export it properly on some systems (noted on FreeBSD 4.7 with \'bash\' 2.05b). In this case, you have to do this manually as just described even when using \'bash\' On Win32, environment variables are set via the System Properties menu. .SS Cannot Enter Certain Directories On Win32 Win32 allows file/directory names to contain non-ASCII characters. Python, as shipped, defaults to ASCII only and grumbles mightily when it is asked to deal with a string containing characters with ordinal values greater than 127 (i.e., 8-Bit "extended" ASCII). The solution to this problem is to enable Python to handle non-ASCII strings. This is done by editing a file called "site.py". This file is normally found in: .nf C:\\Program Files\\PythonXX\\Lib .fi Where "XX" is the actual version of Python you're running. Open this file with an editor and look for the following text: .nf encoding = "ascii" # Default value set by _PyUnicode_Init() if 0: # Enable to support locale aware default string encodings. import locale .fi Change the .B if 0: statement to .B if 1: and the problem will disappear. .SS Getting Command Results Displayed In A New Window When you invoke a command via \*(TW (whether via a command definition in the \*(CF or the keyboard shortcut), you generally want it to run in a new window. This turns out to be tricky on Unix-like systems. If the program you are running is GUI-aware, this should not be a problem. However, if you are using \*(TW to run a command line program or script, you have to take extra care in the formulation of the Command String. In the case of Unix-like systems you have to invoke the command so that it runs in some GUI context. Say you want to use a pager like \'less\' to view files. You would expect that this entry might do it: .nf V view less [DSELECTIONS] .fi Sadly, this will not work, at least not the way you expect. If you started \*(TW from a terminal session and use the command above, it will work, but the results will appear in the invoking terminal window, .B not in a new window as you might expect. If you started \*(TW from a GUI or disconnected it from the initiating terminal with a \'nohup\' ... & invocation, you will get .B no output. This is not a \*(TW problem, it is innate to how command line programs run under Unix shell control. To achieve the desired results, you have to create a new GUI window in which your command can run and display results. The easiest way to do this is to run your command in a new \'xterm\' window like this: .nf V view xterm -l -e less [DSELECTIONS] .fi Some program further require you to provide a shell so they can execute correctly. For instance, running \'ls\' in a command definition requires something like this: .nf L lshome xterm -l -e bash -c 'ls / | [$PAGER]' .fi In fact, this idiom is so common, you will see variables defined in the example \'.twander\' file to simplify such definitions (comments removed): .nf SHELL = bash -c VSHELL = [XTERM] [SHELL] XTERM = xterm -fn 9x15 -l -e .fi Now you can write the command above like this: .nf L lshome [VSHELL] 'ls / | [$PAGER]' .fi This causes your command line program to execute in an \'xterm\' context .B and under a shell interpreter. This is not as much an issue on Win32 systems where the first form of the command above works fine. Win32 appears to have no problem invoking a new window whether the command is GUI-aware or not. However, .B which terminal window is used for output can be confusing. If you start \*(TW from a terminal session, all terminal output will be sent to .B the terminal session you used to invoke the program. The way to work around this is to start \*(TW from a Win32 shortcut, using \'pythonw.exe\' rather than \'python.exe\'. Now each time you run a command that needs a terminal session for output, Win32 will automatically create that session for you. .SS Using Shell Wildcards In Command Definitions The [PROMPT:...] Built-In Variable is provided to make it possible to write general-purpose commands which interact with the user. For example, you might want to define a directory listing command for Win32 like this: .nf L DirList dir [PROMPT:Directory Of What?] | more .fi When the user presses the "L", they are presented with a dialog box into which they enter their directory name or wildcard pattern such as "\\*.bat" and everything works as expected. On Unix-like systems, however, this does not work as expected. Suppose we define the command for these systems to be: .nf L DirList [VSHELL] 'ls -l [PROMPT:Directory Of What?] | [$PAGER]' .fi This works fine .B so long as the user does not enter a wildcard pattern in response to the prompt. Why? Recall that \*(TW quotes all Built-In Variable substitutions by default. If the user enters this at the prompt: .nf /kern* .fi The command \*(TW tries to execute is: .nf VSHELL Stuff ... 'ls -l "/kern*" | ... pager stuff .fi The argument to \'ls\' is double-quoted. The Unix shells understands this kind of quoting to mean .B no expansion of wildcard characters is to be done, which is the exact opposite of what we want. You might think that the easy way to solve this problem is to turn off argument quoting with the -t command line flag. However, this is not really practical. Quoting is on or off globally in the program. Turning it off means no Built-In Variable substitutions will be quoted. That's fine so long as no directory/file you select via the user interface has a space in the name. However, you are almost certain to run into such files sooner or later. (Recall that the only way to deal a directory/file name with spaces in it as a single argument is to quote that name.) So, we need a way to leave quoting on but also properly deal with wildcard string entries from the user. Fortunately, because of the richness of Unix shells, there is a simple way to do this: we'll use a "shell variable" to hold the user's response and the shell's ability to handle multiple commands on one line separated by semi-colons: .nf L DirList [VSHELL] 'UsrResp=[PROMPT:Directory Of What?] ; ls -l $UsrResp | [$PAGER]' .fi Why does this work? Because the shell interprets (and drops) the double-quotes, when the results of the [PROMPT:...] are .B assigned to "UsrResp". The later reference to "$UsrResp" returns just the string the user entered without the quotes and the command works as expected. Interestingly, this problem does not occur when entering text via the RUNCMD dialog. (default: Control-z) Here the text you enter is .B not part of a Built-In Variable substitution, so it is not quoted. (The exception, of course, would be if you entered a [PROMPT:...] reference in the RUNCMD dialog. In this case, the same problem we've just described could occur.) .SS Modal Operation Of New Windows Notice our example commands above do not end with "&". These should not be needed on either Unix-like or Win32 operating systems. When a command is executed, \*(TW starts a new thread of execution which runs concurrently with \*(TW itself. This means you should be able to continue using \*(TW while the new command executes. If not (\*(TW is locked out while the new command runs - so-called "modal" operation), it means your system does not completely or correctly implement threading. In this case, try adding this statement to your \*(CF: "USETHREADS=False" which will force \*(TW to invoke new commands using conventional (heavyweight) process spawning. .SS Windows Don't Disappear On Command Completion It appears that some X Windows implementations (noted on XFree86 / FreeBSD) do not correctly destroy an \'xterm\' window after a command initiated with -e terminates. This is not a \*(TW problem. The workaround is to add the following statement to your Configuration File: "USETHREADS=False" This forces conventional (heavyweight) process spawning when a command is run. This mechanism correctly destroys the window upon command completion. .SS Program Behavior Incorrect When A Window Is Resized Certain Unix programs such as \'less\' appear to not work correctly when the window in which they are running is resized. The program seems to not be properly informed that the window size has changed. This seems to be an interaction caused by running such programs as threads rather than processes. Once again, the workaround here is to set "USETHREADS=False" in the \*(CF. .SS Really Slow Response Times When Changing To A New Directory You may occasionally see .B really slow response times when you change to a new directory. This occurs when you enter a huge directory with thousands of file or subdirectory entries. \*(TW has to to compute the detail information for each of these entries and this can take a lot of time. On a fast machine with modern hard drives and controllers, \*(TW is able to process several thousand entries in just a second or two. However, a number of factors can significantly slow down this process: .IP \(bu 4 The Autorefresh interval is set too low. Processing the directory takes so long that as soon as one refresh finishes, the next starts right away. The program will appear to hang. There are two possibilities here. Either disable autorefreshing (via the -r command line option or the AUTOREFRESH \*(CF option), or set the REFRESHINT value to some high number so that \*(TW has plenty of time to process a directory before the next refresh occurs. .IP \(bu 4 Slow disk drives. You can really watch \*(TW grind if you change to a large directory on a CDROM, for instance. There is no good solution here. These drives are inherently slower than hard drives, and you just have to wait. Make sure you lengthen your refresh interval as described in the previous bullet. .IP \(bu 4 By far the worst culprit here, though, is when running Win32 with \*(W3 options enabled. It takes a lot more work to get win32all-style information about each directory entry, than the default Unix-style information. Simply turning off \*(W3 features alone can speed up directory processing by a factor as high as 4X. .P When you combine these factors, it is possible to get really long processing times. One test situation we observed was reading a directory with over 4000 entries on a Win32 CDROM. With \*(W3 processing enabled this took over a minute. By disabling these features, the time came down to under 30 seconds. .SS Your \*(CF Does Not Produce The Desired Results It's easy to fall into the trap of treating the \*(TW configuration capabilities as a real "programming language". It is not, it is a fairly simple macro language that does very little more than string substitutions. Keep the following rules in mind as you edit your configuration: .IP \(bu 4 Except for conditional tests, Environment Variables and User-Defined Variables are never resolved .B until they appear in a Command Definition. .IP \(bu 4 The Right Hand Side of Option Statements, Key Binding Statements, Directory Shortcut Statements, and Wildcard Statements .B are treated literally - No variable substitution is ever done there. .IP \(bu 4 A Conditional Statement always involves a variable .B reference, never just a variable name. .IP \(bu 4 For a Conditional Statement to be true, the referenced variable .B must be defined and any equality test must be satisfied. .IP \(bu 4 When testing for the existence of a User-Defined or Environment Variable, \*(TW does not care what .B value the variable contains. It is perfectly permissible to have either type of variable set to an empty string. The fact that the variable exists at all is what makes the following construct true: .nf CondVar = \&.if [CondVar] .... \&.endif .P Common mistakes include: .nf ##### # Trying to embed a variable where it will never be resolved ##### DIRSC03 = [$SystemDrive]\\Program Files MYCOLOR = blue FCOLOR = [MYCOLOR] ##### # Expecting a conditional variable to be resolved before the test # Suppose $EDITOR is set to "/usr/local/bin/emacs" ... # # The following will be False because [EDT] equals # the string "[$EDITOR]". It is not replaced # with "/usr/local/bin/emacs" until [EDT] appears # in a Command Definition ##### EDT = [$EDITOR] \&.if [EDT] == /usr/local/bin/emacs ... \&.endif # Note, however, that *this* would work because # Environment Variables are permitted in conditionals ... \&.if [$EDITOR] == /usr/local/bin/emacs ... \&.endif ##### # A badly formed condition is ignored (after a warning) # which means *all the lines following will be processed* # (until a valid condition statement which is False is # encountered). ##### PROCESS = no SUBPART = no # We meant not to process the following but all the # lines up to the next .if statement *are* processed # because the bad syntax on the next line means it's ignored \&.if PROCESS != no ... # Processed! \&.if [SUBPART] == yes # *Now* we'll stop ... \&.endif \&.endif .fi .SH OTHER File/Directory name sorting is done without-case sensitivity on Win32 systems because the underlying operating system does not observe case. Because this program has not been tested on anything other than Unix-like and Win32 systems, command execution by double-click or pressing Enter is inhibited on all other operating systems by default. You must have Python 2.2 or later installed as well as Tkinter support installed for that release. In the case of Win32, Tkinter is bundled with the standard Windows Python distribution. In the case of Unix-like systems, you may have to first install Python and then the appropriate release of Tkinter. This is the case, for example, with FreeBSD. You must install the \*(W3 extensions if you want to use the advanced Win32 features. You'll find the latest version, and occasionally, Release Candidates of the next version of \*(TW at: .nf http://www.tundraware.com/Software/twander .fi You should check this site regularly for updates and bug-fixes. The \'WHATSNEW.txt\' file describes changes since the last public release of the program. .SH BUGS AND MISFEATURES As of this release, a number of problems relating to \*(TW use have been noted: .IP \(bu 4 The \*(CF parser does no validation to check the sanity of its various entries for Program Options, Key Bindings, Directory Shortcuts, Variable Definitions, and Command Definitions. It is entirely possible to edit something into this file that makes no sense at all and causes \*(TW to misbehave. .IP \(bu 4 There appears to be a Tkinter/Tk bug on Unix which sometimes inhibits the correct title display when you tear-off a menu. This is a cosmetic defect and may disappear in future releases of Tkinter/Tk/X-Windows. .IP \(bu 4 Some \*(W3 features do not work correctly or at all on older Windows OSs. For example, the free/total space available in the Drive List View has been noted to display incorrect values on Win98. Similarly, the owner and group names are displayed as "Unavailable" on pre-NTFS file systems. These are OS limitations which \*(TW handles gracefully. .IP \(bu 4 If you are using \'bash\' as your Unix shell, be aware that, although it sets HOSTNAME automatically, this environment variable appears to not be exported consistently on all systems. .IP \(bu 4 If you are running Win32 and have file or directory names with non-ASCII characters in them, you must configure Python to properly deal with such characters. This is described above in the section entitled, .B GOTCHAS. .IP \(bu 4 This program has not been tested on MacOS. Please let us know how/if it works there and any issues you discover. .SH INSTALLING \*(TW Installation of \*(TW is fairly simple and takes only a few moments. The most important thing before installing the program is to make sure you have Python 2.2 (or later) with Tkinter support installed on your system. One other note: However you install the program, it is probably easiest to get started by editing the example ".twander" file to taste. Be aware that this file is shipped with everything commented out. You have to uncomment/edit the section relevant to your operating system: Unix-like or Win32. .SS Installing Using The FreeBSD Port If you've installed \*(TW using the FreeBSD port, all you have to do is copy the example \*(CF, ".twander" found in /usr/local/share/doc/twander to your home directory and edit it to taste. Make sure that /usr/local/bin is in your path. To start the program, just type "twander.py" from the shell prompt. .SS Installing Manually On A Unix-like System Copy the "twander.py" file to a directory somewhere on your path. (/usr/local/bin is a good candidate). Make sure this file has permissions 755 and owner/group appropriate for your system (root/wheel, root/root, or bin/bin). Copy the ".twander" file to your home directory and edit to taste. To run the program, just type "twander.py" from a shell prompt. .B Red Hat Linux Users Please Note: RH Linux (and possibly other Linux systems) installs two versions of Python. Version 1.52 is called \'python\', and Version 2.2 is called \'python2\'. \*(TW requires the latter and will not run on the former. As shipped, \*(TW invokes Python with the Unix shell "#!" mechanism using the name \'python\' - which in this case is the wrong version. You can work around this problem one of several ways: .IP \(bu 4 Rename \'python\' to \'python1\' and then rename \'python2\' to \'python\'. (Not Recommended - could break other programs.) .IP \(bu 4 Write an alias or shell script which explicity starts \*(TW with the correct version of Python: .nf #!/bin/sh python2 twander.py $* .fi .IP \(bu 4 Change the first line of the \*(TW code to refer to \'python2\' instead of \'python\'. .P Red Hat users who have upgraded from earlier Linux versions should also note that you may have files in your home directories owned by owners and groups which are no longer defined in the system! \*(TW shows the owner and group fields for such files as numbers rather than names. As best as we can determine, this is caused when an RH installation is updated from an older version. .SS Installing Manually On A Win32 System Copy the "twander.py" file to a directory somewhere on your path, or create a new directory to hold this file and add that directory path to the PATH environment variable. .B IMPORTANT NOTE TO WIN32 USERS: Windows has the old MS-DOS legacy of assuming that a "." begins a file "extension". Although you can create and read files in the form ".something", it is not recommended because many Win32 programs get confused when they see this. It is also difficult to remove files named this way with the standard Windows programs and utilities. This is especially the case for older Win32 operating systems like Win98. For this reason, it is recommended that you rename the ".twander" default \*(CF provided in the program distribution to something else like "twander.conf" and use the \*(TW -c command line option to point to this \*(CF. On Win32, where to put the \*(CF raises an interesting question. Microsoft operating systems normally do not set the "HOME" environment variable, because they have no notion of a "home" directory - Well, they do, but it is called "USERPROFILE" not "HOME". So, you can either create a new user-specific environment variable called HOME yourself (which points to your desired home directory) or you can invoke \*(TW with the -c argument to explictly declare where it can find its \*(CF. You can run the program several ways on Win32 systems: .IP \(bu 4 Create a Win32 shortcut which points to the "twander.py" file using the "pythonw" command to invoke it. Normally, starting a Python program from the Windows GUI creates a parent window which persists as long as the program runs. Using "pythonw" instead of "python" to run your program suppresses the creation of this blank parent window. For example, you might have something like this in the "Target:" field of your shortcut: .nf "C:\\Program Files\\Python22\\pythonw.exe" C:\\twander.py \\ .fi This runs the program starting at the root directory of the current drive (assuming "twander.py" is located in C:\\. .IP \(bu 4 Start a command line window and issue a command like the one above directly from the command line. .IP \(bu 4 Use Windows Explorer (or better still, an already running instance of \*(TW!) to navigate to the directory where "twander.py" is located. Double-click on the file. If Python is properly installed, there should be an association for ".py" file types and \*(TW should start automatically. .SH GETTING HELP: THE \*(TW MAILING LIST TundraWare Inc. maintains an email list for \*(TW users to get help and exchange ideas. To subscribe, send mail to: .nf majordomo@tundraware.com .fi In the body (not the subject line) of the email, enter the following text, substituting your own email address as indicated: .nf subscribe twander-users your-email-address .fi .SH DESIGN PHILOSOPHY Graphical User Interfaces (GUIs) are a blessing and a curse. On the one hand, they make it easy to learn and use a computer system. On the other, they are a real inconvenience to experienced users who are touch typists. Taking hands off the keyboard to use the mouse can really slow down a good typist. Nowhere is this more apparent than in filesystem browsers. In one corner we have the GUI variants like \'Konqueror\' and \'Microsoft Windows Explorer\'. These are very easy to use but you pretty much need the mouse in your hand to do anything useful. In the other corner are the text-based file browsers like \'List\', \'Norton Commander\', and \'Midnight Commander\'. These are really efficient to use, but have limited functionality and generally do not operate very well on .B groups of things. Both of these approaches also suffer from the well-known interface problem of "What You See Is .B All You Get" - Each program has a predefined set of commands and the user cannot easily extend these with their own, new commands. \*(TW is another approach to the filesystem navigation problem which embraces the best of both the GUI-based approach and the text-based approach. It also provides a rich mechanism whereby each user can easily define their own command set and thereby customize the program as they see fit. This is done with a number of key features: .TP 1) The .B Navigation of the filesystem is graphical - you can use the mouse to select files, directories, or to change directories. However, each major filesystem navigational feature is also doubled on the keyboard (using Control keys) so you can move around and select things without ever touching the mouse. .TP 2) \*(TW also supports a number of .B navigation shortcuts. It provides single control-key access to changing directories, moving to the previous directory, moving up one directory level, moving to any previously visited directory, (de)selecting any or all files/directories in the current view, and escaping to the operating system to run a command. Some (but not all) of these features are also doubled via GUI/mouse operations. .TP 3) There are .B no built-in file or directory commands. All commands which manipulate the files or directories selected during navigation are user-defined. This Command Definition is done in an external \*(CF using a simple but powerful command macro language. This means that that the command set of the program can easily be changed or expanded without having to release a new version of \*(TW every time. Better still, every different user can have their own command set defined in a way that suits their style of working. Best of all, commands can be invoked either graphically (with a mouse click) or via a single keypress to minimize moving your hands off the keyboard. .TP 4) Because \*(TW is written in Python using Tkinter, the same program runs essentially identically on many Unix-like and Win32 systems. The only thing that may need to be changed across these various platforms are the Command Definitions in the configuration file. You only need to learn one interface (and the commands you've defined) across all the different systems you use. .P The consequence of all this is that \*(TW is an extremely powerful and highly customizable filesystem navigator. Once learned, both navigation and command execution are lightning-fast (or at least, as fast as your machine can go ;) while minimizing dependency on the mouse. .SH COPYRIGHT AND LICENSING \*(TW is Copyright(c) \*(CP TundraWare Inc. For terms of use, see the twander-license.txt file in the program distribution. If you install \*(TW on a FreeBSD system using the 'ports' mechanism, you will also find this file in /usr/local/share/doc/twander. .SH AUTHOR .nf Tim Daneliuk twander@tundraware.com .fi