#!/afs/ece/usr/tcl/bin/wish -f
# The next line is executed by most shells, but not Tcl \
wish $0 $*


set Bind_Keyword [file tail [info script]]
source "[file dirname [info script]]/../aux/frame.tcl"

# Help text.
set Help "" ; append Help {Completh -- Teach various string completion techniques to widgets

Many programs, including shells like tcsh offer string completion of things like
file names. This tool teaches textual widgets (currently text, entry, and canvas
widgets) how to complete strings in many different ways.

} $TH_Bindings_Help {

Widgets of Completh

File for Completion Entry / Add Button (Word from File:.. menu item)

Enter a filename onto this entry, and press the button, and a new menu item
will appear on the Completion Functions menu. This menu item will allow words
to be completed using words from the file specified on the entry.


List for Completion Entry / Add Button (Item from List:.. menu item)

Putting a list of items on this entry and pressing the button creates a new menu
item on the Completion Functions menu. This menu item will allow lines to be
completed using the list specified. For example, if the entry contains the items
{True False}, then you can complete 'T' to True.


'Use Widget for Completion' button

When this option is selected, completh allows you to select a widget in any
application. Then it adds a menu option to the Completion Functions menu to
allow you to use that widget as a source for completion. When teaching
completion to a program using a widget for source, that widget must be in the
same program learning completion. The type of completion varies on the type of
widget used as source, and are described below.


Completion Functions Menu

This is a menu showing all the types of completion available. Each type has a
checkbutton associated with it, if an item's checkbutton is on when a widget is
taught completion, that item's function will be included. For example, a widget
will be taught to expand glob patterns, if the 'Expand Glob Patterns' menuentry
is turned on.

Whenever completion is invoked, the functions that are available are called in
descending order. If a function yields no completions, the next one is called.
The program beeps if no functions yielded any available completions. For
example, if the 'Complete Command' and 'Complete Filename' options are turned
on, and completion is invoked when the text before the cursor reads: "[inf", the
command completion will yield the completion "[info", since that is a legitimate
Tcl command. If completion is invoked when the text reads "[x", and there is no
Tcl procedure starting with 'x', but there just happens to be a file called
"[xfigs]" (which is usually a tricky feat in itself), then the completion will
yield "[xfigs]". If no file exists and no Tcl command exists, then the program
will beep.

There is a set of functions that get put on the completion menu upon starting up
completh. More completion functions can be added using the other widgets. But
first, let's examine the functions immediately available.

  Complete Filename

To use this function, enter a filename preceded by a space, and this function
will attempt to complete it. For example " ma" might return " makefile".

  Complete Variable

To use this function, enter a Tcl global variable, preceded by a $ in usual Tcl
fashion, and this function will attempt to complete the variable. Or you can
enter an array and part of an index, and this function will complete the index.
For exmple: $en can complete to $env and $env(D can complete to $env(DISPLAY))

  Complete Command

To use this function, enter an open bracket and part of a Tcl command. This
function will complete the command, and if the string is ambiguous, you can view
all the possible Tcl commands. For example, [in will beep because both [incr and
[info are legitimate completions.

  Expand Glob Patterns

To use this function, enter a glob pattern after a space. This function will
replace the glob pattern with all the files it expands to. For example, " *.c"
might return " foo.c bar.c".

  Value of Variable

To use this function, enter a Tcl global variable preceded by a $ in the usual
Tcl fashion, or array. This function will replace the variable with its value.
If you enter an array variable, this function will append an '(' to it, so you
can then do array index completion. For example, $argc might return 2, and
$env(DISPLAY) might return ":0.0", and $env might return $env(.

  Result of Tcl Command

To use this function, enter a Tcl command between a pair of brackets, and after
hitting the close bracket, do completion, and the command will be replaced with
the Tcl command's result. The program will beep if an error results from the
code. Invoking the dialog will then bring up an information window showing the
error message. For example, [info tclversion] might return '7.4'.

  Filename Completion

This takes the text from the line's beginning to the cursor. This means all the
text on an Entry widget, or a Canvas item, or all the text on one line of a Text
widget before te cursor. It interprets that text as the beginning of a file or
path name, and tries to complete it, just like the 'Complete Filename' option
described above.

  Command Completion

This takes the text from the line's beginning to the cursor, assumes it is part
of a Tcl command and tries to complete that command, just like the 'Complete
Command' option described above.

  Abbreviation Lexicon

This function allows words to be completed. A word is any set of letters,
numbers, or undescores, or any single character that is not a letter, number, or
underscore. This function is associated with an abbreviation list, consisting of
abbreviations, such as 1st, 2nd, 3rd, and words, such as first, second, third.
If the text before the cursor is an abbreviation, the appropriate word is
substituted, so you can type 1st, and replace it with first. You must type the
abbreviation exactly as it is stored to expand it.

Right now completh does not provide a way to specify what abbreviations are
available...see the Bugs section for how to add them.

  Text Widgets (Word from: ...)

This, and other subsequent menu entries don't exist upon startup, but if you
select the 'Use Widget for Completion' button, and select a text widget, this
entry gets added, using that text widget. The text widget is searched for a word
that begins the same way as the word just typed in the widget with completion
(which may be the same text widget!). As soon is it finds a match, the
incomplete string is completed to the same word. Example, 'th' might complete to
this, the, then, etc. depending on the contents of the widget. Unlike most
functions, this one does not search for all completions immediately, but returns
upon finding one. You can invoke completion again to find a different
completion, or you can invoke the completion dialog to see all possible
completions. If the text widget contains a lot of text, this might take a while
to generate.

  Entry Widgets (Word from: ...)

This works the same way, except that an entry widget is used as the source text
instead.

  Message Widgets (Word from: ...)

This also works the same, except that the text from a message widget is used as
the source instead.

  Menu Widget (Entry from: ...)

If you select a menu (not a menubutton!) that menu is searched for an entry
whose text label starts with the line before the cursor. No submenus are
searched.

  Menubutton Widget (Entry from: ...)

If you select a menubutton, its menu, as well as all of its submenus are
searched for an entry whose text label starts with the line before the cursor.

  Listbox Widget (Line from listbox: ...)

The listbox is searched for an item that matches the text on the line before the
cursor.

  Canvas Widget (Text from canvas: ...)

The canvas is searched for a text item whose text matches the text on the line
before the cursor.

} $TH_Frame_Help {

This program uses only one set of global variables per application. This can
cause confusion if you have two widgets with completions defined, and switch
between them often; they may get confused about each other's outstanding
completion possibilities. To avoid this, only request the completion listbox
immediately after invoking a completion in the same widget (which is the natural
thing to do anyway).

Right now there is no way to specify the abbreviation lexicon for the
abbreviation function. The lexicon must be specified in the remote application
by some explicitly code. Here is an example:

set TH(Completion,Wordlist) {{first} {second} {third} {fourth} 
	{Internal Revenue Service} {government}}
set TH(Completion,Abbrevs) {{1st} {2nd} {3rd} {4th} {IRS} {govt}}

If a widget has multiple binding tags bound to the completion bindings, only
the completions bound to the first bindtag get executed, whether they find
a legitimate completion or not.}


# Gives app all the code necessary to do our functions.
proc teach_code {} {
  global Class App Widget
  if {[lsearch "Entry Text Canvas" $Class] < 0} {return ""}

  include_files {complete.Misc.tcl th_string_complete_multiple} \
	[list "complete.$Class.tcl" "th_[set Class]_complete_multiple"]

  foreach cf [form_menu_selected Completions] {
    if {[lindex $cf 0] == "th_word_replace"} {
      include_files {complete.word.tcl th_word_replace}
    } else {
      include_files {complete.line.tcl th_string_global_value}
  }}

  upvar 1 class_flag cl
  if {![catch "set cl"] && !$cl} {set w $Widget} else {set w $Class}

  do_cmd_set TH(Completions,$w) [form_menu_selected Completions]
}

proc widget_bindings {} {
  global Class App Menu_Flag Bindings
  if {[lsearch "Entry Text Canvas" $Class] < 0} {
    return ""}
  set completions [list [form_menu_selected Completions]]
  if {$completions == "{}"} {return ""}

  return $Bindings(Completion)
}


# Adds completion from a file
proc add_file_completion {} {
  global File_For_Completion
  if {!(($File_For_Completion != "") && [file exists $File_For_Completion] && \
      [file readable $File_For_Completion])} {
    bell ; return}
  add_to_form_menu Completions [list "Word from file: $File_For_Completion" \
	[list th_word_replace $File_For_Completion ""]]
}

proc completion_source_widget {} {
  global App Widget Class
  switch $Class {
    "Text" - "Entry" - "Message" {
      set option [list "Word from text in $Widget" \
	[list th_word_replace $Widget ""] $App]
    } "Canvas" {
      set option [list "Text from canvas: $Widget" \
	[list th_line_complete [list th_canvas_text_entries $Widget] none] $App]
    } "Listbox" {
      set option [list "Line from listbox: $Widget" \
	[list th_line_complete [list th_listbox_entries $Widget] none] $App]
    } "Menubutton" {
      set option [list "Entry from menus in: $Widget" [list th_line_complete \
		[list th_cascading_menu_entries $Widget] none] $App]
    } "Menu" {
      set option [list "Entry from menu: $Widget" \
	[list th_line_complete [list th_menu_entries $Widget] none] $App]
    } detault {set option "" ; bell}}

  if {$option != ""} { add_to_form_menu Completions $option }
}


create_form_menu Completions \
 {"Complete Filename" {th_substring_complete th_filter_glob { }}} \
 {"Complete Variable" {th_substring_complete th_filter_vars {$}}} \
 {"Complete Command" {th_substring_complete th_filter_cmds {[}}} \
 {"Expand Glob Patterns" {th_substring_replace th_string_glob_files { }}} \
 {"Value of Variable" {th_substring_replace th_string_global_value {$}}} \
 {"Result of Tcl Command" {th_substring_replace th_string_tcl_result {[}}} \
 {"Filename Completion" {th_line_complete th_filter_glob none}} \
 {"Command Completion" {th_line_complete th_filter_cmds none}} \
 {"Abbreviation Lexicon" {th_word_replace "" ""}}
for {set i 7} {$i <= 9} {incr i} {set Menu(completions,$i) 0}

create_form_entry .filecomp "File for Completion" File_For_Completion ""
button .filecomp.use -text "Add to Menu" -command add_file_completion
pack .filecomp.use -side right -before .filecomp.e
lappend TH(Completions,.filecomp.e) {th_line_complete th_filter_glob none}

create_form_entry .listcomp "List for Completion" List_For_Completion ""
button .listcomp.use -text "Add to Menu" -com {add_to_form_menu Completions \
	[list "Item from List: [lrange $List_For_Completion 0 1]..." [list \
	th_line_complete [list th_list_completions $List_For_Completion] none]]}
pack .listcomp.use -side right -before .listcomp.e

pack [button .widgetcomp -text "Use Widget for Completion" -command {
	if {![get_widget]} {bell} else {completion_source_widget}}] -fill x
