FindWords utility

ulis, 2004-01-20. FindWords is an editor utility that let you jump to another occurrence of the selected string. Designed for Textpad it can be used (modified) for other editors.

Modified 2004-01-25: Added quotes around the $Sel arg to deal with strings containing spaces.

Modified 2004-01-26: Added keyboard support.

A snapshot

The script

  # FindWords: jump to a string (Textpad utility)
  # (C) 2004, ulis; NOL
  # -----------------------
  # input: source file, selected string
  # output: exec of a script

  if 0 \
    # Abstract

    This script let you see a listbox with all the occurrences of the selected string,
      select one of them and have the text cursor positionned in front of the selected one.

    # Install

    For Textpad:

      - Optional: copy the DDEOPN32.EXE utility from the Textpad system directory
        to the Windows sytem32 or system directory
      - Put FindWords.tcl & FindWords.cfg in the TextPad system directory (or elsewhere)
      - With the /Configure/Preferences menu add a new tool with:
          - Commands        = <the path of wish>
          - Parameters      = <the path of this file> $File "$Sel"
          - Initial folder  = $FileDir
        (see example below)
      - Rename this tool: FindWords
      - Modify FindWords.cfg
        exec = <script to execute to go to the word> (where you can have to set the DDEOPN32.EXE path)
                 inside this script: 
                    %FileName% will be replaced by the Tcl source file name
                    %Line% will be replaced by the line where is the string.
                    %Char% will be replaced by the number of the char where start the string.
        options = <space separated list of key/value pairs>
                 -over <boolean>
                    0 : don't search for overlapping strings
                    1 : search for overlapping strings
                 -thick <integer>
                    thickness of the closeness (in chars)
        @exec, @options : Tcl script returning a value for exec or options
        (see example below)
      - Optional: add a key accelerator with /Configure/Preferences/Keyboard/Categories/Utilities

    # Use

    From the Tcl/Tk text window select the /Tools/FindWords menu
    Click the right line inside the listbox

  # ------------------
  # GetWords
  # ------------------
  # returns a list of lines description
  #   in the form of {line-# char-# closeness}
  # ------------------

  proc GetWords {} \
    variable {}
    # get string
    set string $(string)
    # open Tcl source file
    set fn $(fn)
    if {![catch { open $fn } res]} { set fp $res } \
    else { tk_messageBox -message $res; exit }
    # loop thru lines
    set l 1
    if {$(-over)} { set l [string len $string] }
    set strs {}
    set lines {}
    set ln 0
    while {![eof $fp]} \
      # get a line
      set line [string trimleft [gets $fp]]
      lappend lines $line
      # search for the string inside the line
      set cn 0
      while {$cn > -1} \
        set cn [string first $string $line $cn]
        if {$cn > -1} \
        { lappend strs [list $ln $cn]; incr cn $l }
      # update current line number
      incr ln
    close $fp
    set (strs) $strs
    set (lines) $lines

  # ------------------
  # GotoWord
  # ------------------
  # exec's the (script) to go to the string
  # ------------------

  proc GotoWord {} \
    variable {}
    set index [.lb curselection]
    if {$index == ""} { return }
    foreach {ln cn} [lindex $(strs) $index] break
    set fn [string map {\\ /} $(fn)]
    set script [string map [list %FileName% $fn %Line% [incr ln] %Char% [incr cn]] $(exec)]
    catch { eval $script } msg
    if {$msg != ""} { tk_messageBox -message $msg }

  # ------------------
  # SetParms
  # ------------------
  # sets the (keys) & (script) parameters
  # ------------------

  proc SetParms {} \
    variable {}
    set (exec) "exec DDEOPN32.EXE Textpad \"%FileName%(%Line%,%Char%)\""
    set (opts) {-font {Courier 9} -over 0 -thick 10}
    set fn [file join [file dirname [info script]] FindWords.cfg]
    if {![catch { open $fn } res]} { set fp $res } \
    else { tk_messageBox -message $res; exit }
    while {![eof $fp]} \
      set line [string trimleft [gets $fp]]
      switch -glob -- $line \
        ""    -
        \;*   -
        \[*   -
        #*    { set key "" }
        exe*  { set key exec }
        opt*  { set key opts }
        @exe* { set key @exec }
        @opt* { set key @opts }
        default \
        { tk_messageBox -message "unknown key \"$line\" in config file"; exit } 
      if {$key == ""} { continue }
      set n [string first = $line]
      if {$n == -1} \
      { tk_messageBox -message "wrong line \"$line\" in config file"; exit } 
      set value [string trim [string range $line $n end] " ="]
      set ($key) $value
      if {[string match @* $key]} \
        set key [string range $key 1 end]
        set ($key) [$value]
    close $fp
    # get options
    foreach {option value} $(opts) \
      switch -glob -- $option \
        -f*     { set key -font }
        -o*     { set key -over }
        -t*     { set key -thick }
        default \
        { tk_messageBox -message "unknown option \"$option\" in config file"; exit } 
      set ($key) $value

  # ------------------
  # ChooseWord
  # ------------------
  # presents the listbox with all strings occurrence
  # ------------------

  proc ChooseWord {} \
    variable {}
    wm withdraw .
    set (fn) [lindex $::argv 0]
    set (string) [lindex $::argv 1]
    if {$(string) == ""} \
    { tk_messageBox -message "word is empty"; exit } 
    if {[llength $(strs)] == 0} \
    { tk_messageBox -message "no word found"; exit } 
    wm deiconify .
    wm title . "FindWord \"$(string)\""
    listbox .lb -font $(-font) -yscrollc {.vs set} -activestyle none
    scrollbar .vs -command {.lb yview}
    grid .lb -row 0 -column 0 -sticky nsew
    grid .vs -row 0 -column 1 -sticky ns
    grid rowconfigure . 0 -weight 1
    grid columnconfigure . 0 -weight 1
    bind .lb <ButtonRelease-1> GotoWord
    bind .lb <KeyPress-Return> GotoWord
    set height 0
    set width 0
    set t $(-thick)
    set l [string length $(string)]
    set l [expr {$l + $t - 1}]
    foreach item $(strs) \
      foreach {ln cn} $item break
      set line [lindex $(lines) $ln]
      set n1 $cn; incr n1 -$t
      set n2 $cn; incr n2 $l
      set string [string range $line $n1 $n2]
      set item "$ln,$cn: $string"
      .lb insert end $item
      incr height
      set length [string length $item]
      if {$width < $length} { set width $length }
    if {$height > 40} { set height 40 }
    if {$height < 10} { set height 10 }
    if {$width > 80} { set width 80 }
    if {$width < 20} { set width 20 }
    .lb config -width $width -height $height
    .lb selection set 0
    focus -force .lb
    focus -force .
    raise .

  # ========
  # let's go
  # ========

  package require Tk

Configuring the tool in TextPad

The config file

  # ---------------------
  # FindWords config file - example 1
  # ---------------------
  # DDEOPN32.EXE directory is in the path

  exec = exec DDEOPN32.EXE Textpad "%FileName%(%Line%,%Char%)"
  options = -font {Courier 9} -over 0 -thick 15


  # ---------------------
  # FindWords config file - example 2
  # ---------------------
  # DDEOPN32.EXE is in c:/Program Files/Textpad

  exec = exec c:/Program Files/Textpad/DDEOPN32.EXE Textpad "%FileName%(%Line%,%Char%)"
  options = -font {Courier 9} -over 0 -thick 15

[name redacted] your introduction seems to imply that it's OK to modify and reuse this code. Maybe it would be a good idea to make an explicit statement to that effect next to the (c) notice (this being a Wiki, I could add such a statement, but somehow it doesn't seem right :-) ).

ulis In short: yes you can. See Who owns the content on this Wiki. The used licence, NOL, means: No Obligation Licence. For you, as for me.

(name redacted) : I know I can, but in many cases we shouldn't make modifications. For instance, editing the terms of use of someone else's source code qualifies as evil for me. Thanks for the clarification, I wasn't familiar with the acronym, NOL.

ulis Sorry for the misunderstanding: I don't speak english and have to survive with that. And yes the ability(?) given to everybody to change the content of the Wiki has some moral limits (we are between gentlemen ;^)).

LV well, gentlebeans anyways...

!Help wanted- I am trying to run FindWords.tcl on Text Pad 4.4.0 and it simply does not react. I am doing something wrong. What? Please contact me at [email protected]. Thanks. JGL

Dear ulis, thank you for your prompt answer.

I have received your mail, but I cannot answer you per mail, I get mail deamon failure notices:


so I will put my answer & question here:

 > Please send me more information about your trouble with FindWords.
 > Did you have difficulties following the installation instructions?

Yes, I think so.

Maybe I don't understand:

 >           - Commands        = <the path of wish>
 >           - Parameters      = <the path of this file> $File "$Sel"
 >           - Initial folder  = $FileDir


 >       - Modify FindWords.cfg
 >         exec = <script to execute to go to the word> (where you can have to set the DDEOPN32.EXE path)

My Textpad is in:


The DDEOPN32.EXE is in:


The tcl and cfg are in:


(This is not my standard clip library - the other tcls are elsewhere)

I have opened /Configure/Preferences/Tools/Add/Programm and added


  ulis: you needed to put here the path of wish (the Tcl/Tk interpreter), something like:
  C:/Program Files/Tcl/bin/wish.exe


C:\Text\TextPad\Extras\FindWord\FindWords.tcl $File "$Sel" as parameter

  ulis: this seems correct, you needed to put:
  C:/Text/TextPad/Extras/FindWord/FindWords.tcl $File "$Sel"

I do not really know what to do with the FindWords.cfg

  ulis: you only have to insure that DDEOPN32.EXE can be called with the exec line

(I have tried changing

  exec = exec DDEOPN32.EXE Textpad

"%FileName%(%Line%,%Char%)" to

  exec = exec C:\Text\TextPad\SYSTEM\DDEOPN32.EXE

Textpad "%FileName%(%Line%,%Char%)"

  ulis: this seems correct, you needed to put:
  exec = exec C:/Text/TextPad/SYSTEM/DDEOPN32.EXE TextPad "%FileName%(%Line%,%Char%)"

but with no effect)

or what else I should change.

 > Did you tried to use a newer version of TextPad?
 > Last version is 4.7.3.

Not yet. Is that the reason?

  ulis: no, I think not.

 > Which OS are you using?

Windows 98

OK thanks I'll try once more


TF: In your post above you refer to C:/Program Files/Tcl/bin/wish.exe path. Where can we find a version of this file for download?

TF: I found one in the end, suitable for installation on Windows XP - ' '

ulis: The version you donwloaded is outdated and might not work with my script. The official Tcl/Tk page is . To download a binary version of Tcl/Tk for Windows, follow one of the links:

  New version Tcl/Tk 8.5a3: 
  Stable ActiveTcl

Category Editor utility