Morten Skaarup Jensen asked in news:comp.lang.tcl :
I am writing an editor and would like to know a simple way to find out if the text widget has been edited since the file was loaded so that one knows whether or not to save.
Ro: If using 8.4 there is also the modified flag in the text widget
For earlier versions of Tk:
Bryan Oakley replied:
Override the text widget command, and look for insert and delete subcommands. Here's a quick hack off the top of my head. Don't take this as an example of particularly good coding style, but it does illustrate the point.
D. McC: See below for an example
To run it through its paces, just run the following code. Type and/or delete something in the text widget and notice how the save button becomes enabled. Click the save button to simulate saving the data and note how it becomes disabled. Also notice how this works even if you cut or paste data into the widget.
frame .toolbar pack .toolbar -side top -fill x -expand no button .toolbar.save -text "save" -command doSave -bd 1 pack .toolbar.save -side left text .text pack .text -side top -fill both -expand yes rename .text .text_ proc .text {command args} { global textModified # let the real text widget do all the real work set result [uplevel .text_ $command $args] if {[string equal $command "insert"] \ || [string equal $command "delete"]} { set textModified 1 } return $result } proc doSave {} { global textModified # pretend we've saved the text... # reset the state set textModified 0 } proc updateUI {args} { global textModified if {$textModified} { .toolbar.save configure -state normal } else { .toolbar.save configure -state disabled } } trace variable textModified w updateUI set textModified 0
Man, I love writing Tcl, but sure do miss writing Tk. Tk is just sooooo nice! I literally haven't written this much tk code in a year :-(
D. McC: Now, about that "modified" flag (which came in with Tk 8.4) in the text widget--here's an example of usage. I bind the <Key> and <Button-2> events to execute this "saveup" procedure:
proc saveup {} { if { [.textinhere edit modified] } { bind .textinhere <Key> {} bind .textinhere <Button-2> {} wmtitulo save } }
All this does is to (1) stop the bindings from firing needlessly and (2) invoke a procedure ("wmtitulo save") to display a "Save Changes?" indicator on the titlebar. Then, when the contents of the text widget (.textinhere) are saved and the "Save Changes?" indicator vanishes, the "modified" flag is set to zero:
.textinhere edit reset .textinhere edit modified 0
MDD: A much simpler approach:
D. McC: i.e., simpler than my old approach, which I've now deleted in favor of what's above
set changed 0 bind .your.text.widget <Any-KeyRelease> {set changed 1} ... if {$changed == 1}{Save_File_Proc; set changed 0}
Of course, this will give a false positive if you use non-editing keys in the text widget, such as moving the cursor with the arrow keys, but it's not a bad quick-and-dirty solution.
It also doesn't catch instances where your code adds text to the widget via some other means, such as selecting "paste" from a pulldown menu.
MDD: Right. That's the "dirty" part. ;-)
Would you get around that with:
bind .your.text.widget $event {set changed 1}
That would propably mean even more false positives, though.
Ctext: provides an edit modified command; which can be used to tell if data has been inserted/deleted. It also works with the 8.3 and possibly 8.1 Tcl/Tk releases.
ABU 2005-10-19:
Other than the "modified" flag, the text widget also generates a virtual event <<Modified>>. Here an excerpt from the command reference manual:
Using this technique there are no "false positive", and it is not limited to catch 'interactive' changes. The only thing you need to remember is that you need to "reset" the "modified-flag" after detecting changes.
Here you can find a little demo:
# DEMO pack [text .txt] set feedbackMsg "No Changes" proc resetModifiedFlag {w} { global feedbackMsg $w edit modified false set feedbackMsg "No Change" } button .b1 -text reset -command "resetModifiedFlag .txt" label .l1 -textvariable feedbackMsg -bg red pack .b1 .l1 -side left -expand 1 # load some data ... .txt insert end "aaa\nbbb\nccc" # reset modified-flag resetModifiedFlag .txt bind .txt <<Modified>> { bell ; set feedbackMsg "Changed !" } # start playing ...
Blendernut: Just what i needed. Thank you, ABU.
WJG 2006-03-17: I tried using the above binding in an app which has been starpacked. The <<Modify>> event didn't work. Anyone any ideas why not? The starpack is for Tk8.5a.
MG: The binding is <<Modified>>, not <<Modify>> like you said there. Check and make sure you're using the right one in your code?
LES I have an app that handles it this way:
# earlier in the code... set ::FILECONTENT [$::w::text get 1.0 end] # later... proc p.check_changes {} { set content_now [$::w::text get 1.0 end] if {$content_now != $::FILECONTENT} { $::w::statusbar configure -background #BA0000 -foreground #FFFFFF $::w::statusbar configure -text "FILE CHANGED. PRESS CTRL+S TO SAVE OR ALT+Q TO CLOSE." } } # later... bind $::w::text <Key> {after idle [list p.check_changes]}