First, a quick background on widgets. When you create a widget in Tcl, two things happen:[ol][li]Tcl creates and initializes the internal data structure of the widget[/li][li]Tcl create a new command with the same name as the widget[/li][/ol]You perform most of your interaction with the widget by executing the widget command, and telling it some operation to perform. For example:
(The
pack and
destroy commands are two exceptions to this general rule, because they can both accept multiple widget names as arguments.)
The code shown by
ulis is a common technique for doing specialized processing with the Text widget. We're in essence slipping a pre-processor in front of the standard widget command.
The first step is to rename the widget command, which is what the
rename command does. Then, we create our pre-processor, using the original name of the widget command. Then throughout the rest of our script, it looks as though we're simply calling our standard widget command to interact with the widget.
As for the pre-processor itself, we're defining
.t to require at least one argument (
cmd) and to accept 0 or more additional arguments, which will be stored as a list in the variable
args. (Using
args as the last parameter is the way you create procedures in Tcl that accept a variable number of arguments.)
Then, we use the
switch command to determine if we're trying to do an
insert or
delete operation on the widget. This is safe way of detecting changes, because all of the standard methods for inserting or deleting text (even keyboard interaction such as cut and paste) eventually end up calling the
insert or
delete operations. If we detect a change, we flag the change by setting the global variable
changed to true. (By the way, the "::" preceding the variable name is another way of refering to a global variable, even if you don't explicitly use a
global command from within a procedure.)
Then, we simply call the original widget command, passing the operation (
cmd) and all other arguments that were provided. In this case, we need the
eval command to properly "break up" the list of arguments stored in
args, because the original widget command is expecting to see separate arguments on the command line, not a list.
I'd make a slight change to
ulis's
switch command to guard against syntax errors caused when a user incorrectly tries to give an operation beginning with a "-" character. The
switch command is a bit stupid and thinks that any argument beginning with a "-" is an option for it (like
-regexp). Admittedly, any such operation argument would produce a usage error when passed to the widget command, but I'd prefer to display the widget command's error rather than the
switch command's syntax error in a case like this. So, my updated version would be:
Code:
proc .t {cmd args} {
switch -exact -- $cmd {
insert -
delete { set ::changed 1 }
}
return [eval _.t $cmd $args]
}
By the way, you could also automatically execute some code whenever the the Text contents change by creating a variable trace on the
changed variable. In this example, a write trace would be sufficient. A simple example is:
Code:
proc TextChanged {var elem op} {
puts "The text has changed!"
set $var 0
}
trace variable ::changed w TextChanged
- Ken Jones, President
Avia Training and Consulting
866-TCL-HELP (866-825-4357) US Toll free
415-643-8692 Voice
415-643-8697 Fax