#!/afs/ece/usr/tcl/bin/wish -f

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

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.

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.

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.

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. With that text, it performs file globbing, and
completes as much of the filename as possible. It will beep if no completions
exist, or the current text refers to more than one filename, you can then
request the completion dialog to show the available filenames, and perhaps,
select one.

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.3'.

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 indec completion. For example, $argc might return 2, and
$env(DISPLAY) might return ":0.0", and $env might return $env(.

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".

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.

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 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". This
function differs from the Filename Completion function in that this function
will complete a filename preceded by a space, while the other one requires the
filename to be at the beginning of a line.


'Use Widget for Completion' Checkbutton 

When this button is turned on, completh does a grab and 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. The possibilities are as follows:

Text Widgets (Word from: ...)

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
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.


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.

} $TH_Frame_Help {
This program uses only one set of global variables per application. This can
cause confusion if you have two widgets with tab completion, 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
in the following example code:

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


# Gives app all the code necessary to do our functions.
proc teach_code {app widget} {
  if {[widget_bindings $app $widget] == ""} {return}
  set class [send $app winfo class $widget]
  include_files $app {common.tcl th_beep} \
	{complete.Misc.tcl th_string_complete_multiple} \
	[list "complete.$class.tcl" "th_[set class]_complete_multiple"]

  foreach cf [completion_functions $app] {
    if {[lindex $cf 0] == "th_word_replace"} {
      include_files $app {complete_word.tcl th_word_replace} \
	{browse.Misc.tcl th_string_wordstart}
      if {(![catch {send $app winfo class [lindex $cf 1]} result]) && \
		($result == "Text")} {
        include_files $app {search.Text.tcl th_Text_string_forward}}
    } else {
      include_files $app {complete.line.tcl th_string_global_value}
  }}

  teach_binding_code $app Complete_Word
}

proc widget_bindings {app widget} {
  if {[lsearch "Entry Text Canvas" [send $app winfo class $widget]] < 0} {
    return ""}

  set completions [list [completion_functions $app]]
  if {$completions == "{}"} {return ""}

  global Bindings
  return [regexp_replace $Bindings(Completion) %COMP $completions]
}


# Returns the completion functions selected in the menu.
proc completion_functions {app} {
  global Completion Completion_Fns
  for {set i 0} {$i < [llength $Completion_Fns]} {incr i} {
    if $Completion($i) {
      if {([llength [lindex $Completion_Fns $i]] > 2) && \
	([lindex [lindex $Completion_Fns $i] 2] != $app)} {return ""}
      lappend completions [lindex [lindex $Completion_Fns $i] 1]
  }}
  if {[catch "set completions"]} {return ""} else {return $completions}
}

# Create the completions menu.
proc completion_menu {m} {
  catch "destroy $m"
  menu $m
  set i 0
  global Completion_Fns Completion
  foreach fn $Completion_Fns {
    $m add checkbutton -label [lindex $fn 0] -variable Completion($i)
    incr i
}}

# Extend toggle_grab to kill  the teach_widget button
proc toggle_grab_source {} [concat [info body toggle_grab] { ;
  global Teach_Active_Widget ; set Teach_Active_Widget 0
}]

# Extend teach_codebindings to select a widget to use as a completion source.
proc source_widget {x y b class_flag} {
  global Teach_Active_Widget Teach_Active
  if {!$Teach_Active_Widget} {
    teach_codebindings $x $y $b $class_flag
    return
  }
  if {![which_widget $x $y 1 app widget]} {
    toggle_grab_source ; th_beep ; return}
  completion_source_widget $app $widget
  toggle_grab_source
}

# Adds widget as a completion source to completion functions menu.
proc completion_source_widget {app widget} {
  set class [send $app winfo class $widget]
  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 "" ; th_beep}}

  if {$option != ""} { add_completion $option }
}

# 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])} {
    th_beep ; return}
  add_completion [list "Word from file: $File_For_Completion" [list \
      th_word_replace $File_For_Completion ""]]
}

# Add a option to the completion menu
proc add_completion {option} {
  global Completion_Fns Completion
  set i [llength $Completion_Fns]
  lappend Completion_Fns $option
  .complete.menu.m add checkbutton -label [lindex $option 0] \
	-variable Completion($i)
  set Completion($i) 1
}


# Standard completion functions
set Completion_Fns {
 {"Abbreviation Lexicon" {th_word_replace "" ""}}
 {"Command Completion" {th_line_complete th_filter_cmds none}}
 {"Filename Completion" {th_line_complete th_filter_glob none}}
 {"Result of Tcl Command" {th_substring_replace th_string_tcl_result {[}}}
 {"Value of Variable" {th_substring_replace th_string_global_value {$}}}
 {"Expand Glob Patterns" {th_substring_replace th_string_glob_files { }}}
 {"Complete Command" {th_substring_complete th_filter_cmds {[}}}
 {"Complete Variable" {th_substring_complete th_filter_vars {$}}}
 {"Complete Filename" {th_substring_complete th_filter_glob { }}}
}


frame .complete ; pack .complete -side top -fill x
menubutton .complete.menu -menu .complete.menu.m -text "Completion Functions"
pack .complete.menu -side right
completion_menu .complete.menu.m
checkbutton .complete.widget -text "Use Widget for Completion" \
	-command toggle_grab -variable Teach_Active_Widget
pack .complete.widget -fill x

create_form_entry .filecomp "File for Completion" File_For_Completion ""
bind .filecomp.e <Tab> "th_Entry_complete_multiple %W [list [concat \
       {{th_line_complete th_filter_glob none}} $Entry_Completions]]"
button .filecomp.use -text "Add to Menu" -command add_file_completion
pack .filecomp.use -side right -before .filecomp.e

create_form_entry .listcomp "List for Completion" List_For_Completion ""
button .listcomp.use -text "Add to Menu" -command {add_completion \
	[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

bind all <Any-Button>                   "source_widget %X %Y %b 0"


