Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

How to add a document listener 1

Status
Not open for further replies.

fabien

Technical User
Sep 25, 2001
299
AU
Hello!

I would like to know how to add a document listener in a text widget to check if the the text has been modified for instance.

Thanks

Fabien
 
Try this

set ::changed 0
pack [entry .e -textvariable ::changed]
pack [text .t]
rename .t _.t
proc .t {cmd args} { switch $cmd { insert - delete { set ::changed 1 } }; return [eval _.t $cmd $args] } }

The global variable ::changed should be changed if the text is updated.

ulis
 
Ulis,

Thanks a lot, I tested it and it worked fine. However, I am not sure that I understand how the function works (and why you have to rename .t) , can you please give me more details on this.

Thanks again,

Fabien
 
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:

Code:
.t configure -wrap word

(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
 
thanks a lot Ken, great explanation!

Fabien
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top