tkBindExtended v1.0 beta1 - enhanced bindings for Tk4.0 Paul Raines **** WARNING: this is still to be considered BETA software **** INTRODUCTION: The included files contain replacement bindings for the text and entry bindings of the standard library of the Tk4.0 distribution. It is a highly extended version of the original which adds many new emacs-like bindings. These include - multi-level undo that handles tags (no embedded windows yet) - argument keys - "hard" auto-filling and paragraph filling with prefixes - a mark ring - multi-level kill buffer (Copy,Cut,Paste) which handles tags - xterm-like mouse bindings - incremental or dialog search ( optional package ) - rectangle editing ( optional package ) - support for emacs-like minibuffer for text widgets The included entry.tcl file also has undo, argument keys, kill buffer, and xterm-like mouse bindings. Basically, it has all the same bindings as the text widget that don't affect multiple lines or tags. Run the included tkBindTest script with this README file as an argument to demo the new bindings. This script also gives one a good idea how to use the enhanced text bindings in ones own applications. See the included file emacs.list for complete status of the emacs bindings. If one has tk_strictMotif set, they will have the following MAC-like bindings instead of those listed in emacs.list. Undo: C-z Cut: C-x Copy: C-c Paste: C-v Select All: C-/ For emacs bindings, all the Meta bindings can also be accessed using the Escape key as a "state" key. Implementing a "state" keys was kind of tricky and I am sure not fully bug free so if you see any irregularities using them, please report them to me. The "state" keys are Escape, C-x, C-q, and to a certain extent the isearch C-s and C-r keys. INSTALLATION: Just created a directory (possibly /usr/local/lib/tkbind) and copy all the files in the distribution there. That is it! To use these bindings on selected Tk programs, you should add the following lines at beginning of the Tk scripts. You must of course provide the proper path to the extended binding scripts. # if the enhanced bindxtnd.tcl is not the default, source it if {[info proc tkBindDefVar]==""} { foreach key [bind Text] { bind Text $key {} } foreach key [bind Entry] { bind Entry $key {} } source /usr/local/lib/tkbind/bindxtnd.tcl source /usr/local/lib/tkbind/text.tcl source /usr/local/lib/tkbind/entry.tcl # source additional packages here } If you choose to have these files actually replace the default Tk4.0 library files, follow the preceding steps (sorry, no Makefile yet). At this point I do not suggest doing this as the software is still experimental and I feel that there are many ways Tk applications can create conflicts with these bindings (especially if they use the bindtags command). 1) Make a backup of the tk.tcl, text.tcl and entry.tcl files in your TK_LIBRARY directory. 2) Copy all *.tcl files from distribution to the TK_LIBRARY directory. 3) Apply the following patch to the tk.tcl file there. *** tk.tcl.orig Sun Jul 16 17:37:18 1995 --- tk.tcl Tue Sep 12 12:30:36 1995 *************** *** 78,83 **** --- 78,84 ---- # Read in files that define all of the class bindings. # ---------------------------------------------------------------------- + source $tk_library/bindxtnd.tcl source $tk_library/button.tcl source $tk_library/entry.tcl source $tk_library/listbox.tcl 4) You are done. Now test it out. CUSTOMIZATION: The standard text.tcl file is sourced before the individual user has any chance to override the bindings made there. One problem I discovered is that many keyboards have only an Alt modifier key and no Meta modifier key. Rebinding all the bindings to bindings in ones ~/.wishrc file is a pain and not very efficient. Therefore, I have allowed for the existance of a ~/.tkbindrc file in which one may put the line set tkBind(meta) Alt so that the bindings are used in place of bindings. One can also set any of the other non-widget specific tkBind() settings here. See the top of the text.tcl file for a list. It is important that you get the tkBind(modKeys) setting correct, so make sure you check its list against 'xmodmap -pm'. The default list is set tkBind(modKeys) [list Control_L Control_R Meta_R Meta_L Alt_R \ Alt_L Shift_L Shift_R Caps_Lock Multi_key \ Super_L Super_R] Note that the ~/.tkbindrc file is also the place to set the tk_strictMotif variable to your liking since text.tcl is sourced before any scripts. Read the top of the bindxtnd.tcl file for a complete table of tkBind() settings. Since the ~/.tkbindrc file is sourced before any of the default bindings are made, it cannot be used at the global level to override these bindings with your own. However, you can define procedures named tkTextInitHook and/or tkEntryInitHook which will be run (if it exists) after all the default bindings are made and standard procedures are created. These procedures takes no arguments. This hook is the ideal place to require any add-on packages such as isearch and to make your own bindings. Another hook, tkTextWidgetHook, will be run (if it exists) after the widget specific setup for a newly created text widget is done. It takes the text widget pathname as its sole argument. There is also a tkEntryWidgetHook hook. See the included example.tkbindrc file and also read the sections on MULTI-KEY BINDINGS and ARGUMENTS IN PERSONAL BINDINGS below. ISEARCH PACKAGE: The included isearch package gives the standard emacs C-s and C-r isearch keys for the text widget. No regexp support at present. Also no search ring capability, but C-s, C-r, BackSpace, Delete, C-g, C-j, C-w, C-y, and C-q should work in isearch mode as expected. Unlike emacs isearch, unsuccessful additions to the search are not added to the search string anyway. To get the isearch package, place the line 'tkBindRequire isearch' in the tkTextInitHook procedure in your ~/.tkbindrc file. See CUSTOMIZATION above and the example.tkbindrc file. DSEARCH PACKAGE: The included dsearch package gives binds the C-s and C-r keys to popup a dialog doing searchs. This has an additional advantage over isearch in that it can do replaces and handle regular expressions. To get the dsearch package, place the line 'tkBindRequire dsearch' in the tkTextInitHook procedure in your ~/.tkbindrc file. See CUSTOMIZATION above and the example.tkbindrc file. PROMPT PACKAGE: The prompt package is designed mainly as a library of routines for other packages to use for prompting the user for information. Currently, only the rectangle package uses it. The application using tkBindEnhanced may also find it useful. It does define two bindings of its own. C-x i Insert file at current position M-x Prompt for Tcl command to eval Normally, a transient dialog is popped up to request the information but if a MesgBuffer is present for the affected widget, then it will be used instead. See EMACS-LIKE MINIBUFFER below. NOTE: documentation on use of package for developers is coming soon. RECTANGLE PACKAGE: The included rectangle package defines bindings for edit operations on rectangles of text. Use of these routines will have unpredictable results in non-fixed fonts are used. Correct processing of tabs is not yet implemented. The following bindings should work as in standard emacs C-x r o open-rectangle C-x r y yank-rectangle C-x r d delete-rectangle C-x r k kill-rectangle C-x r c clear-rectangle C-x r t string-rectangle (requires prompt package) C-x r r copy-rectangle Not a copy to a register but just a copy to the normal rectangle kill buffer. This may change once a register package is written. To get the rectangle package, place the line 'tkBindRequire rectangle' in the tkTextInitHook procedure in your ~/.tkbindrc file. See CUSTOMIZATION above and the example.tkbindrc file. UNDO: *** NOT ON BY DEFAULT *** The default maximum number of levels is 50. It doesn't yet handle embedded windows. The undo ring also does not cycle. One must use the wrapper procedures tkTextDelete, tkTextInsert, tkTextReplace, and tkTextReTag in order to key the undo tracking in sync. Using the normal 'text insert', 'text tag add', 'test tag remove' and 'text delete' commands will invalidate undo tracking. (NOTE: I will document these procedures soon.) For the above reasons, the undo feature is not on by default. One can make a call to the tkTextUndoSetup procedure to turn it on for a specific widget. When a large change is made to a text widget (such as an editor app deleting the whole buffer and replacing it with the contents of a new file) it will be more efficient to use the normal text commands and make a call to the tkTextUndoSetup procedure again to reinitialize undo tracking for the widget. One can make undo on by default for all text widgets by setting the tkBind(bindUndo) settings to 1 in the ~/.tkbindrc file. This isn't recommended unless you have modified all your Tk apps to follow the rules above. The tag information stored is only the tag's name. If the tag's configuration is changed, a later undo reinstating text with that tag will have the new characteristics. One might consider this a feature :) If multiple delete/insert/tag operations are to be considered one operation from an undo perspective, the developer should use tkTextUndoBeginGroup and tkTextUndoEndGroup procedures. Each operation still counts individually toward the maxUndo limit. NOTE: some of the tkText* procedures have a side-effect of moving the insert cursor. I might be convinced of changing this if it proves a problem to users. All the above (except tag stuff) applies to the Entry widget also, with the appropriate change of name for the procedures. EMACS-LIKE MINIBUFFER: The text.tcl, entry.tcl and compliant packages write information to the global variable tkBind(%W,mesg) where %W is the text or entry widget. It would be most advantagous for developers to use the -textvariable option of a entry, message or label widget packed below the text widget to act as a message area for the tkText procedures. The name of this variable can be changed for each widget using the tkTextSetMesgVar procedure. This can allow several widgets to use the global variable for messages. For example, pack [label .l -textvariable bindmesg -anchor w \ -wraplength 0 -height 1] -side bottom -fill x tkBindSetMesgVar .t bindmesg tkBindSetMesgVar .e bindmesg However, to get the best emacs compliance, a fully functioning minibuffer-like widget packaged underneath the text widget is desired. This is possible with the tkBindCreateMesgBuffer procedure which operates like a widget creation command. For example pack [tkBindCreateMesgBuffer .m -foreground blue] -side bottom -fill x The possible widget options are the same as those for the label widget which deal with color, font, cursor or border. Use of any other option might break it. One created, text and entry widgets can be connected to it with the tkBindAttachMesgBuffer command. Example. # set up entry and text to use label for message echo tkBindAttachMesgBuffer .t .m tkBindAttachMesgBuffer .e .m If one of these MesgBuffers is attached to a widget, the prompt package will use it instead of popping up separate window. MULTI-KEY BINDINGS: Doing multi-key bindings in tk4.0 by using the bind command with sequences such as has proven to have many problems. I have decided that they are evil and should be avoided at all costs. Instead, I use a bindtags method of achieving the same effect. Here is how it works. Say you want to be a state key like in emacs. Then do bind Text { tkBindSetStateKey %W TextCC C-c } The 2nd argument to tkBindSetStateKey is a bindtag that will be used for bindings as you will see below. The last argument is a "message" that will be put in the tkBind(%W,mesg) variable when the key is pressed as a prompt for a second key. Then to bind any key to the Control-c map, one simply uses a call to the bind command like bind TextCC { sendmessage %W set tkBind(%W,arg) {} set tkText(%W,prevCmd) SendMessage } The last two lines in the binding are recommended to be consistent with the emacs-style API. To report unbound key sequences it is also suggested that one make the following bindings for the above example. bind TextCC { if {[lsearch $tkBind(modKeys) %K] > -1} break set $tkBind(%W,mesgvar) "C-c [tkBindGetMod %s]%K not bound." eval $tkBind(bell) } bind TextCC { set $tkBind(%W,mesgvar) "C-c [tkBindGetMod %s]mouse-%b not bound." eval $tkBind(bell) } Stacked multi-key bindings are possible as in the following. bind TextCX { tkBindSetStateKey %W TextCXR {C-x r} } bind TextCXR { tkBindRectangleKill %W } bind TextCXR { tkBindRectangleYank %W } ARGUMENTS IN PERSONAL BINDINGS: One can use the argument mechanism in personal bindings using the tkBindDefArg procedure. Simply make a call like set n [tkBindDefArg $w $n] in your binding to get the argument. The $w is the affected widget and the input $n may be either a decimal number, "+" or "-". If it is a decimal number, that number is returned. If it is "+", the current user argument is returned. If it is "-", the negative of the current user argument is returned. If there is no current user argument, then it defaults to 1. This can be changed with an optional third argument to tkBindDefArg. Possibilities are summarized as follows: RETURNS Call current arg is X no current arg ------------------- ---------------- -------------- tkBindDefArg $w + X 1 tkBindDefArg $w - -X -1 tkBindDefArg $w 2 2 2 tkBindDefArg $w + 3 X 3 tkBindDefArg $w - 3 -X -3 tkBindDefArg $w 2 3 2 2 One should really make a call to tkBindDefArg even if no argument is going to be used in order to clear the current argument. One can also set the tkBind(%N,arg) to empty explicitly OTHER NOTES: 1) PARAGRAPH FILLING Attempts to be intelligent and handle prefixes like # or >>. This is probably pretty fragile so please test it for me. 2) KILL RING Default maximum of 20 kills. It doesn't yet handle embedded windows. 3) XTERM-LIKE MOUSE BINDINGS Any selection made with the mouse will go into Tk's private clipboard as well as the X server's PRIMARY selection. When mouse button 2 is pressed to do a paste, it will first look for a PRIMARY selection and if it doesn't find one, it will use the Tk clipboard. You should find this behavior similar to the way xterm works. One difference from xterms is the behavior of button 3. Instead of extending the current selection (shift-B1 can be used for this purpose) it can be used to make a selection with out moving the insert cursor. This is convenient for copying text elsewhere in the windo to the current insertion point with a B3-drag then B2-click operation. TODO: * Handle embedded windows for text widgets * More extensive set of Mac-like bindings and may other bindings styles * A package for query-replace bindings * A package for register handling * Better docs for users and developers * A package for shell operations on buffers COPYRIGHT: Copyright 1995 by Paul Raines (raines@slac.stanford.edu) Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The University of Pennsylvania, Stanford University, and Stanford Linear Accelerator Center makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. Parts of this packages are also covered by the following as noted: Copyright 1992-1994 by Jay Sekora. All rights reserved, except that this file may be freely redistributed in whole or in part for non-profit, noncommercial use. Copyright (c) 1992-1994 The Regents of the University of California. Copyright (c) 1994-1995 Sun Microsystems, Inc. See the file "license.terms" for information on usage and redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. DISCLAIMER: UNDER NO CIRCUMSTANCES WILL THE AUTHOR OF THIS SOFTWARE OR THE UNIVERSITY OF PENNSYLVANIA, STANFORD UNIVERSITY, THE STANFORD LINEAR ACCELERATOR CENTER, OR THE DEPARTEMENT OF ENERGY BE HELD RESPONSIBLE FOR ANY DIRECT OR INCIDENTAL DAMAGE ARISING FROM THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION. THE SOFTWARE HEREIN IS PROVIDED "AS IS" WITH NO IMPLIED OBLIGATION TO PROVIDE SUPPORT, UPDATES, OR MODIFICATIONS. Please mail any suggestions, bugs, whines to raines@slac.stanford.edu