A change-sensitive text widget

Difference between version 34 and 35 - Previous - Next
** Summary **

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.''
''(If usam writing 8.4an theditore is alsnd would thlike mtod know a sifimpled flwagy to find out if the 
text widget --[Rhas been edited since the file was lo])aded so that one knows whether
or not to save.''
** See Also **

    [ANSI color control]:   a value-added text widget -

** Description **

[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 -- [D. McC])''
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 tTcl, but sure do miss writing Tk. Tk is just sooooo
 nice! I 
literally haven't written this much tk code in a year :-(
----
See also [ANSI color control] for a value-added text widget -

----
[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) -- [D. McC])''

======   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.
ItOf alsco doeursn'te, catch instances whill giver a false positive if your coduse anon-edds iting kextys toin the
text widget, viasuch aso moving the cursor with the arrow mkeanys, sbucht ait's selecting "pasote" from a pullbadow
quick-and-dirty mensolution.
[MDD]:It Righalso doesn't. catch Tinstances where your code at'dds text to the "widget via
some other means, such as selecty"ing "parst.e" ;-)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] 19-oct-2005
Other than the "modified" flag, the [textABU] widget also generates a virtual event <<Modified>>.
Here an excerpt from the command reference manual2005-10-19:
Other than the "modified" flag, the [text] widget also generates a virtual
event <<Modified>>.  Here an excerpt from the command reference manual:

    :   '''THE MODIFIED FLAG'''
    :   The text widget can keep track of changes to the content of the widget by means of the ''modified'' flag. Inserting or deleting text will set this flag. The flag can be queried, set and cleared programatically as well. Whenever the flag changes state a `<<Modified>>` virtual [event] is generated. See the '''edit modified''' widget command for more details.
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] (17/03/06) 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.
[MWJG] T2006-03-17:  I tried using the above binding isn <<Modan app whified>>,ch has beeno
starpacked. The <<Modify>> likevent you sadid n'the worek. ChAnyoneck andy makide asure whyou're using othe? rigThe
starpack one ins yfour code?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?

<<categories>> GUI | RS