Version 0 of bind: remove one command

Updated 2019-04-12 15:47:49 by oehhar

HaO 2019-04-12

Scope

The Tk bind command allows to add commands by the syntax:

bind win tag +cmd

There is no buildin way to remove the added command.

Warning

The "+cmd" syntax is considered as broken. Bindtags (which are lists) or tcllib uevent should be used instead.

Nevertheless, I face this issue for important global events like undrowish "<<WillEnterBackground>>" command, which is potentially important for a lot of modules in common.

Observation

The command "bind win tag +cmd" does the following:

  • define "cmd" if no binding exists: "bind win tag cmd"
  • add a newline ("\n") and "cmd", if a binding exists

Pseudocode:

proc BindAdd {w t c} {
    set ccur [bind $w $t]
    if {$ccur eq ""} {
        bind $w $t $c
    } else {
        bind $w $t $c $ccur\n$c
    }
}

Removal function

Here is a proposition to remove my own registration.

# Remove first Cmd from bind pattern.
# @return true if removed
proc BindRemove {Win Pattern CmdIn} {
    set CmdCur [bind $Win $Pattern]
    set Pos 0
    while {-1 != [set Pos [string first $CmdIn $CmdCur $Pos]]} {
        # Check if found string starts at the beginning of after a new-line
        if { $Pos == 0 || [string index $CmdCur $Pos-1] eq "\n" } {
            # Check if found string ends at end or before a new-line
            set PosEnd [expr {$Pos + [string length $CmdIn]}]
            if {    $PosEnd == [string length $CmdCur]
                    || [string index $CmdCur $PosEnd] eq "\n"
            } {
                # Binding found
                if {$Pos != 0} {
                    set CmdNew [string range $CmdCur 0 $Pos-2]
                } else {
                    set CmdNew ""
                }
                if {$PosEnd!= [string length $CmdCur]} {
                    if {$CmdNew ne ""} {
                        append CmdNew \n
                    }
                    append CmdNew [string range $CmdCur $PosEnd+1 end]
                }
                bind $Win $Pattern $CmdNew
                return 1
            }
        }
        incr Pos
    }
    return 0
}

Usage:

proc mymodule::init {} {
    bind . a +[namespace code MyModuleFunction]
}
proc mymodule::delete {} {
    bindRemove . a [namespace code MyModuleFunction]
}