#!/afs/ece/usr/tcl/bin/wish -f
#
# Script to make Elsbeth text editor from Els, a bare-bones file browser.
#

set Tools {menutraverseth dialogth browseth edith filebrowseth fileth focuth
	gridth iterath searcheth windowth resizeth completh parenth commandeth
	marketh taggeth}
set More_Tools {paragrath tclth cth lispth htmlth errorth}
set Number_Of_Frames 4

set Elsbeth_Help {

The Elsbeth Editor

Elsbeth is a complete text editor based on els, a bare-bones text browser. When
you run elsbeth, it first calls els, which takes the command-line arguments and
sets up a text window for each file specified on the command line. Elsbeth then
adds some bindings provided by the Teacher Hypertools, plus a few extras, like
help and quit. We will first describe the functionality of els (which you can
run alone), and then describe each set of bindings provided by the hypertools.

Els takes any number of command-line arguments. Arguments are divided into two
types, options and configs. For each option, Els opens a new window, the option
signifies what to load into that window. Each config modifies the Els windows
for the options that follow it. For example, typing:
	els alpha -b beta -i gamma &
is equivalent to typing:
	els alpha &
	els -b beta &
	els -b -i gamma &

Each option indicates some material to put in th e text widget of an Els window.
The following are valid options:
	0	Nothing is loaded. Useful if Els is run by another program
	X	The X Selection is copied.
	file	The contents of the file indicated by pathname. If this file
does not exist, the text widget is left blank, but the file is remembered as a
new file, so saves automatically go to that file. The first save will create the
new file.
	-	Standard input. This is useful to put Els at the end of a pipe.
		(Example: ls -l | els -)
	=	Standard input, gradual, for those slow commands.
		(Example: make all |& els =)
	|cmd	Output of exec-ing cmd, just like reading an I/O pipeline.
		(Your shell may require you to place this in quotes.)
	|cmd&	Gradual output of exec-ing cmd.
		(Again, your shell will most likely want this in quotes.)

Each config indicates some modification to future options. The following are
valid configs:
 	-i Causes Els to keep future windows iconified.
	-t Title	Adds the next word to Els's window & icon title.
If more than one -t is specified, all the -t titles are concatenated and put
into the title, before Els's usual stuff.
	-w configs	The configs are passed on to the creation of the text
widget. Thus, for example, one could specify the size of the text widget by:
els -w "-height 4 -width 40" ...

	-c cmd		The cmd is executed as a Tcl command after startup. You 
can thereby load initial modules, such as the paragrath module by:
elsbeth -c "add_elsbeth_module paragrath" ...

	-a 		Causes els to open the new files in a new Wish
interpreter. If this option is not specified, and an els (or elsbeth) program is
already running, the new options are passed to the old elsbeth interpreter, and
the new one exits. If specified, the new interpreter ignores the old one, and
starts up windows on its files.

	-H		Causes els to print out a quick help description of its
command-line syntax (similar to what you're reading now). This also happens if
you call els with no command-line arguments.

The startup file checks for the existance of an elsbeth file in the user's
HOME/.th directory, and if it exists, sources it. It also checks for an .elsbeth
file in the current directory and sources it too, if it exists. This enables the
user to customize Elsbeth to his needs. For example, the user may specify when
to load various other modules, such as paragrath.
}


# Repacks all of the non-common widgets of a tool into a lower frame widget.
# Provide a button to show/hide that frame widget.
proc repack {tool i} {
  set f .tool_$i
  set f2 [set f].frame_$tool
  set w [set f2].frame
  set l [set f2].label
  set flag 0

  foreach widget [pack slaves .] {
    if {[lsearch {.buttons .output .bind} $widget] >= 0} {continue}
    if {[string match ".tool_*" $widget]} {continue}

    if {!$flag} {set flag 1
      if {![winfo exists .tool_$i]} {
        frame $f
        pack $f -side top -fill x -expand no
        lower $f
      }
      frame $f2 -relief raised
      pack $f2 -side left -fill both -expand yes -padx 5 -pady 5
      lower $f2
      checkbutton $l -text "[string toupper $tool] Parameters" \
        -variable Visible($tool) -onvalue 1 -offvalue 0 \
        -command "toggle_tool $w $tool"
      pack $l -side top -fill x -expand yes
      frame $w -relief raised
    }

    if {![catch "pack newinfo $widget" info]} {
      set index [lsearch $info "-in"]
      eval pack $widget -in $w [lreplace $info $index [expr $index+1]]
    }
  }
  return $flag
}

proc toggle_tool {w tool} {
  global Visible
  if $Visible($tool) {
    pack $w -side bottom -expand yes -fill both -padx 5 -pady 5
  } else {
    pack forget $w
}}

# Now load all
source "[file dirname [info script]]/../aux/bindings.th/makebeth.tcl"
source "[file dirname [info script]]/../aux/bind.tcl"
set Elsbeth_Bindings $Local_Bindings
set els_help $TH_Bindings_Help
set i 0
set old_help $TH_Bindings_Help
foreach tool [concat $Tools elsbeth $More_Tools] {
  if {$tool == "elsbeth"} {
    append Elsbeth_Help "\n" $els_help
  } else {
    source "[file dirname [info script]]/$tool"
    catch {rename widget_bindings "[set tool]_widget_bindings"}
    catch {rename teach_code "[set tool]_teach_code"}
    catch {lappend Elsbeth_Bindings $Local_Bindings}
    if {$old_help != $TH_Bindings_Help} {
      append Elsbeth_Help "\n" $TH_Bindings_Help
    }
    set old_help $TH_Bindings_Help
    if {[repack $tool [expr $i % $Number_Of_Frames]]} {incr i}
}}
set Local_Bindings $Elsbeth_Bindings

eval tk_menuBar .bind.key_mb [winfo children .bind.key_mb]
underline_menubutton_labels "[winfo name .]" [winfo children .bind.key_mb]
clear_output
set Elsbeth_Flag 0 ; set Frame_Flag 0

# Adjust a few default values
set Search_Select 0 ; set Allow_Pipe 1
set Modified_Button 1 ; set Show(path) 1 ; set Show(name) 1

cd $TH_Dir ; set TH_Dir [pwd]

# Redefine teach_code to teach all the tools' widget code.
proc teach_code {app widget} {
  global Tools
  foreach tool $Tools {
    if {[info procs [set tool]_teach_code] != ""} {
      [set tool]_teach_code $app $widget
}}}

# Redefine widget_bindings to teach all the tools' widget bindings.
proc widget_bindings {app widget} {
  global Tools Frame_Flag
  set bindings ""

# Handle Focuth, and Elsbeth's focus_entries binding.
  global Bindings Class
  append bindings $Bindings(Focus)
  if $Frame_Flag {
    lappend bindings [regexp_replace $Bindings(Elsbeth,Entry) %C $Class]
  } else {
    lappend bindings $Bindings(Elsbeth,Misc)
    set Class [send $app winfo class $widget]
  }

  foreach tool $Tools {
    if {[info procs [set tool]_widget_bindings] != ""} {
      set bindings [concat $bindings [[set tool]_widget_bindings $app $widget]]
  }}

  return $bindings
}

# Teach an app our bindings
proc teach_codebindings {x y b class_flag} {
  if {![which_widget $x $y 1 app widget]} {toggle_grab ; th_beep ; return}
  teach_all_to_widget $app $widget $b $class_flag
}

proc teach_all_to_widget {app widget b class_flag} {
  set bindings [widget_bindings $app $widget]
  if {$bindings == ""} {th_beep ; return}
  clear_output
  teach_code $app $widget
  switch $b {
    1 {if $class_flag {set w [send $app winfo class $widget]
       } else {set w $widget}
      teach_keybindings $w $bindings $app
      teach_entries_bindings $app $widget
  } 2 {teach_tag_bindings $app $widget
       teach_menubindings $widget $bindings $app
       set menu "[set widget]_mb"
       send $app update idletasks
       set menubuttons [send $app winfo children $menu]
       do_cmd $app "tk_menuBar $menu $menubuttons" 0
}}}

proc do_cmd_noerr {app cmd} {
  global Output
  set Output $cmd
  set f [send $app $Output] ; show_output 0
  return $f
}

proc teach_entry_completion {app widget name} {
  global Special_Entries ; incr Special_Entries
  set f [do_cmd_noerr $app "th_show_entry $widget $name\n"]
  teach_keybindings $f.e [completh_widget_bindings $app $f.e] $app
  do_cmd_noerr $app "th_hide_entry $widget $name\n"
}

proc teach_entries_bindings {app widget} {
  global Elsbeth_Flag Frame_Flag Completion Completion_Fns
  global Special_Entries ; set Special_Entries 0
  set l [llength $Completion_Fns]
  for {set i 0} {$i < $l} {incr i} { set comp($i) $Completion($i) ; set Completion($i) 0}
  set Frame_Flag 1

# First add all bindings (including general completion) to entry widgets
  for {set i 4} {$i < 9} {incr i} {set Completion($i) 1}
  if $Elsbeth_Flag {set Completion(3) 1}
  set f [send $app th_show_entry $widget goto]
  teach_keybindings Entry [widget_bindings $app $f.e] $app
  send $app th_hide_entry $widget goto

# Now have the file entry widget also do completion to filename.
  set Completion(2) 1
  teach_entry_completion $app $widget file

# Now have the wish entry widget also do completion on commands
  set Completion(2) 0 ; set Completion(1) 1
  teach_entry_completion $app $widget wish

# Now have the search entry widget complete on the original widget.
  for {set i 1} {$i < 9} {incr i} { set Completion($i) 0}
  completion_source_widget $app $widget
  teach_entry_completion $app $widget search

# Ditto for replace entry widget
  teach_entry_completion $app $widget replace

# Reset completion functions.
  set Completion($l) 0
  for {set i 0} {$i < $l} {incr i} { set Completion($i) $comp($i) }
  set Frame_Flag 0
}

# Creates file 'elsbeth.bindings', which contains Elsbeth's keys and menus.
proc make_elsbeth {} {
  global TH_Dir Elsbeth_Flag Elsbeth_Help Completion Completion_Fns
  global Special_Entries
  global Modified_Button Show Els

  wm iconify . 
  toplevel .msg ; label .msg.m ; pack .msg.m ; 
  wm title .msg "Makebeth Working" ; wm iconname .msg "Makebeth Working"
  .msg.m configure -text "Starting els..." ; update

  set send_me [concat send [list [winfo name .]] \
	{set Els [list [winfo name .]]}]
  set Els ""
  exec "$TH_Dir/bin/els" -a -i -c $send_me 0 &
  tkwait variable Els

  set app $Els
  set Elsbeth_Flag 1
  set tmpl [open "$TH_Dir/aux/elsbeth.tmpl" "r"]
  set elsbeth [open "$TH_Dir/aux/elsbeth.bindings.tcl" "w"]
  set toplevel ".sym1"
  set text "$toplevel.t"
  set l [llength $Completion_Fns]

# Create help file
  .msg.m configure -text "Creating help file..." ; update
  set help [open "$TH_Dir/aux/elsbeth.help" "w"]
  puts $help $Elsbeth_Help
  close $help

# Teach Class bindings (that don't depend on the widget)
  .msg.m configure -text "Teaching class bindings..." ; update
  for {set i 0} {$i < $l} {incr i} {set Completion($i) 0}
  completion_source_widget $app $text
  copy_file_to $tmpl $elsbeth "### BEGIN_Class_Code"
  set modified_button $Modified_Button ; set Modified_Button 0
  set show_path $Show(path) ; set Show(path) 0
  set show_name $Show(name) ; set Show(name) 0
  teach_all_to_widget $app $text 1 1
  set mark [.output index "end - [expr $Special_Entries * 4] lines"]
  puts $elsbeth [regexp_replace [.output get 1.0 $mark] $text "%W"]

# Teach Instance bindings (those that do)
  .msg.m configure -text "Teaching instance bindings..." ; update
  copy_file_to $tmpl $elsbeth "### BEGIN_Instance_Code"
  puts $elsbeth [regexp_replace [.output get $mark end] $toplevel "%TL"]
  set Modified_Button $modified_button
  set Show(path) $show_path
  set Show(name) $show_name
  teach_all_to_widget $app $text 2 0
  puts $elsbeth [regexp_replace [.output get 1.0 end] $toplevel "%TL"]
  global All_Bindings
  puts $elsbeth "th_flash_label %TL.t -relief raised -text \{Press [lindex [lindex $All_Bindings(Help) 0] 0] for help.\}\n"

  global More_Tools Bindings
  copy_file_to $tmpl $elsbeth

  puts $elsbeth "\n"
  .msg.m configure -text "Copying Elsbeth bindings..." ; update
  puts $elsbeth "set Optional_Code(elsbeth) \{"
  clear_output
  teach_binding_code $app All_Shown
  puts $elsbeth "[.output get 1.0 end]"
  clear_output
  do_elsbeth_bindings $app $text $elsbeth $Bindings(Elsbeth)
  foreach tool $More_Tools {
    puts $elsbeth "\n"
    .msg.m configure -text "Copying $tool bindings..." ; update
    puts $elsbeth "set Optional_Code($tool) \{"
    [set tool]_teach_code $app $text
    do_elsbeth_bindings $app $text $elsbeth [[set tool]_widget_bindings $app $text]
  }

# Finish file.
  .msg.m configure -text "Completing elsbeth.bindings..." ; update
  close $elsbeth ; close $tmpl
  set Elsbeth_Flag 0
  destroy .msg
  catch {send $app destroy .}
  wm deiconify .
}

proc do_elsbeth_bindings {app text elsbeth bindings} {
  puts $elsbeth [regexp_replace [.output get 1.0 end] $text "%W"]
  clear_output
  teach_keybindings $text $bindings $app
  puts $elsbeth [regexp_replace [.output get 1.0 end] $text "%W"]
  clear_output
  teach_menubindings $text $bindings $app
  puts $elsbeth [regexp_replace [.output get 1.0 end] $text "%W"]
  clear_output
  puts $elsbeth "\}"
}

# Copies contents of file1 to file2 until file1 exhausted, or line matching
# start is reached. (If start is not given, copies to end)
proc copy_file_to {file1 file2 {start -1}} {
  while {1} {
    if {[gets $file1 line] == -1} {return}
    puts $file2 $line
    if {($start != -1) && ($line == $start)} {return}
}}


# These bindings only should take effect during a global grab.
bind all <Any-Button>                   "source_widget %X %Y %b 0"
bind all <Any-Shift-Button>             "source_widget %X %Y %b 1"

.buttons.source.m invoke 2
button .buttons.compile -text "Make Elsbeth" -command make_elsbeth
pack .buttons.compile -side right -expand yes -fill x

set Help "" ; append Help {Makebeth -- Add all available TH keybindings to a widget.

This program serves to add all Elsbeth bindings provided by the Teacher
Hypertools. It runs several available hypertools, whose functions are described
below. Each function here acts as if that function had been executed in every
loaded hypertool.


Functions} $Elsbeth_Help {

Widgets of Elsbeth

Hypertool Checkbuttons

When turned on, the corresponding hypertool parameters are shown, and you can
change them, to alter the behavior of the stuff that hypertool teaches. When
turned off, the hypertool parameters get hidden once more. Only hypertools with
parameter widgets will have hypertool checkbuttons.


'Make Elsbeth' button

When pressed, this tool generates a file called elsbeth.bindings.tcl, using the
parameters and current bindings known to Elsbeth. The Elsbeth program runs els
on whatever command-line arguments are given it, and then adds the bindings and
menus to every text widget opened (as well as any new ones to get created later)
This generation process may take a minute or two. When complete, an empty text
widget with menus for the text widget are shown. You can't close the text
window, but you can play with elsbeth's functions.

This is basically an automation of the steps described below in the section on
how to make Elsbeth. (The steps are provided here for your information... you
don't need to actually manually execute them on an Els widget.)

} $TH_Frame_Help {
Due to packing limitations, if you expand a hypertool's widgets (by pressing its
parameters checkbutton) with a lower hypertool's widgets already expanded, the
window will get messy. To avoid this, when expanding several hypertools'
widgets, go from top to bottom.


How to turn ELS into ELSBETH:

Although you can run makebeth on any widget, the main job for makebeth is to
make Elsbeth (a complete text editor) out of Els (a small program that brings up
text widgets on files, but adds no bindings or features).

Here are the steps you need to take. These are done automatically by the 'Make
Elsbeth' checkbutton, but are provided here for your convenience.

- Start up Els on a single (writable) file.
- Set makebeth's parameters however you like.
- Set makebeth's 'Source' menu to 'Add to auto_path'. (This is not necessary
  but it makes the code much shorter, and this is the default setting anyway.)
- Copy the file elsbeth.tmpl to elsbeth.bindings.tcl. This will be   your
  bindings file, which you will be modifying soon.

You first want to teach the text and entry widgets the proper keybindings.
- Select 'Teach' and, while holding down the Shift key, click button 1 on
Els's text widget.

- In the elsbeth.bindings.tcl file, find the two lines:
### BEGIN Class_Code
### END Class_Code
and insert the code generated by makebeth between them. Leave out the last 16
lines or so, since they refer to actual widgets (this is class code and should
be widget-independent.)

- Now replace every instance of ".sym1.t" with "%W". (A global search/replace
facility is really handy here.) (Actually, if you see sym2 or sym345, treat it
as if you saw sym1...it should be sym1 if you are only invoking els on one
file.)

- Now take the last set of lines, (that you left out before), and put them
between the two lines:
### BEGIN Instance_Code
### END Instance_Code

- Now replace every instance of ".sym1" with "$tl". (Again, the widget name may
differ. If you are editing one file, this will be the  right widget name.)

- If you don't want menus with your text widgets, then you are done. Otherwise
select 'Teach' and press Button 2 on one of the text widgets.

- In the elsbeth.bindings.tcl file, find the line:
p### END Instance_Code
and insert the code generated by makebeth before it. (This should go right after
the last set of lines mentioned above.)

- Now replace every instance of ".sym1" with "$tl". (Again, the widget name may
differ. If you are editing one file, this will be the  right widget name.)

NOTE: This won't get you the optional modules like paragrath. Use the 'Make
Elsbeth' button to get those.
}


