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


# ELS (Editor Left Simple) This program is very simple; it is meant to be
# expanded to Elsbeth, although it functions decently on its own. It can read in
# files or piped commands into text widgets. Besides normal text bindings, M-q
# destroys the window its done on. Els exits when all windows are destroyed.

# To run this program, you should make sure the top line reflects the location
# of wish on your system.

# Els takes any number of arguments. Args consist of options and configurations
# Each option brings up a separate Els toplevel window. Configs modify all
# subsequent Els windows.


set Els_Help {ELS command-line arguments:
ELS creates one text window for each option:
        0       Create empty window
        X       Fill window with X Selection
        -       Fill window with standard input
        file    Fill window with contents of file
        |cmd    Execute command, load output
ELS takes the following configuration parameters for each option. Any parameter
given before an option applies to that and all future options.
        -w configs      Configure text widget with configs
        -i              Iconify text window
        -t word         Add word to window & icon title
        -c cmd          Execute cmd after set up.
        -a              Run alone, in a new wish interpreter.
        -p              Pile this widget onto the main window
        -H              Print help (this text)
}

# Note that an old Els interpreter may have a different pwd than the one you
# invoke a new Els interpreter in. Els will fix pathnames of filenames, but
# pipes could be problematic. You should probably give absolute pathnames when
# opening pipes with a current Els running. Also if you want to give stdin to
# Els, you have to specify the -a option, since the old Els won't have the same
# stdin.

# If this program shouldn't be allowed to read/write pipes, set the following
# variable to 0
set TH(Pipe,Enabled) 1


# For arbitrary symbol generation

set TH(Symname) "syme"
set TH(Symnum) 0

# Returns a new symbol upon each call
proc els_gensym {} {
  global TH
  incr TH(Symnum)
  return $TH(Symname)$TH(Symnum)
}

source "[file dirname [info script]]/../lib/file.Text.tcl"
source "[file dirname [info script]]/../lib/busy.tcl"

# Make text go busy while reading a file
rename th_Text_read_file th_Text_read_file_2
proc th_Text_read_file {w f {pipe 0}} {th_busy $w th_Text_read_file_2 $w $f $pipe}
rename th_Text_insert_file th_Text_insert_file_2
proc th_Text_insert_file {w f {pipe 0}} {th_busy $w th_Text_insert_file_2 $w $f $pipe}


# Procedures for filling the text widget.

# Duplicate of th_dedot_file in lib/file.Misc.tcl
proc els_dedot_file {p} {
# First eliminate .
  while {[string match {\./*} $p]} {set p [string range $p 2 end]}
  while {[string match {*/\.} $p]} {set p [string range $p 0 [expr [string length $p] - 2]]}
  while {[regsub -- {/\./} $p {/} q]} {set p $q}

# Now eliminate ..
  while {[regsub -- {[^/]*/\.\.} $p {} q]} {regsub -- {//} $q {/} p}
  return $p
}

# Loads w with contents of TH(File,$w)
# if insert_flag is given, then insert, otherwise load.
# Duplicate of th_load_file in lib/file.Misc.tcl
proc els_load_file {w {insert_flag 0}} {
  global TH auto_index
  if {[catch "set TH(Pipe)"]} {set TH(Pipe) 0}
  if {[catch "set TH(Pipe,Enabled)"]} {set TH(Pipe,Enabled) 0}
  if {($TH(Pipe) || ([string first "|" $TH(File,$w)] >= 0)) && !$TH(Pipe,Enabled)} {
    error "Pipe reading (from $TH(File,$w)) is not allowed in this application."
  }

  set pwd [pwd]
  if {([set index [string first "|" $TH(File,$w)]] >= 0)} {
    cd [string range $TH(File,$w) 0 [expr $index - 2]]
    set name [string range $TH(File,$w) $index end]
    set pipe 1
  } elseif $TH(Pipe) {
    cd [file dirname $TH(File,$w)]
    set name [file tail $TH(File,$w)]
    set pipe 1
  } else {  set name $TH(File,$w) ; set pipe 0}
  if {$pipe && ([info procs th_string_complete_multiple] != "") || \
       ([lsearch [array names auto_index] th_substring_replace] >= 0)} {
    if {[set comp [lindex [th_string_complete_multiple $name \
		{{th_substring_replace th_string_glob_files { }}}] 0]] != ""} {
      set name $comp
  }}
  set file [open $name r]

  if {![file writable $TH(File,$w)] && [file exists $TH(File,$w)]} {
    if {([info procs th_flash_label] != "") || \
       ([lsearch [array names auto_index] th_flash_label] >= 0)} {
      th_flash_label $w -text "File is not writable!"
  }}

  if $insert_flag {
    if {![catch "set TH(File,Old,$w)"]} {set TH(File,$w) $TH(File,Old,$w)
    } else {unset TH(File,$w)}
    th_[winfo class $w]_insert_file $w $file $pipe
    set TH(Modified,$w) 1
  } else {
    if {[info procs th_file_update_widgets] != ""} {
      th_file_update_widgets $w}
    if {[info procs th_file_update_titles] != ""} {
      th_file_update_titles $w}
    th_[winfo class $w]_read_file $w $file $pipe
    catch {set TH(Mtime,$w) [file mtime $TH(File,$w)]}
    set TH(Modified,$w) 0
    if {([info procs th_kill_undos] != "") || \
       ([lsearch [array names auto_index] th_kill_undos] >= 0)} {
      th_kill_undos $w}
    set TH(File,$w) [els_dedot_file $TH(File,$w)]
    set TH(File,Last,$w) "r"
    }
  cd $pwd
  close $file
}


# A dummy proc, this is merely to handle the Abort (= Control-g) scenario
# used for interrupting gradual reading.
proc th_bind {w th_binding {cmd ""}} {
  global TH
  if {$cmd == ""} {return [bind $w [lindex $TH(Binding,$th_binding) 0]]}
  foreach binding $TH(Binding,$th_binding) {bind $w $binding $cmd}
}
set TH(Binding,Cancel) {<Control-g>}


# Widget creation / destruction routines

proc els_new_text {pile} {
  set tl ".[els_gensym]"
  if {$pile} {
    frame $tl
  } else {
    toplevel $tl
    wm iconify $tl
  }
  global Els_Command Els_Configs
  eval text $tl.t -relief sunken -setgrid [expr !$pile] $Els_Configs

# This is for elsbeth to add things to.
  frame $tl.t_fm
  pack $tl.t_fm -side bottom -fill x

  pack $tl.t -in $tl -expand yes -fill both

  if $pile {
    global Els_Geometry
    if {[pack slaves .] == ""} {
# First window. Figure out geometry of .
      focus $tl.t
      pack $tl -in . -expand yes -fill both
      set iconified_flag [winfo ismapped .]
      wm withdraw .
      update idletasks
      set x [winfo reqwidth .] ; set y [winfo reqheight .]
      set Els_Geometry "[set x]x[set y]"
      wm geometry . $Els_Geometry
      pack forget $tl
      wm deiconify .
      if $iconified_flag {wm iconify $w}}

    $tl.t configure -height 1
  } else {focus $tl.t}

  if {$Els_Command != ""} {eval $Els_Command}
  return $tl
}

# Destroy toplevel window, exit if all toplevels destroyed.
proc els_destroy_text {t} {
  set class [winfo class [winfo parent $t]]
  destroy [winfo parent $t]

# Quit if that was the last window.
  set end_flag 1
  foreach child [winfo children .] {
    if {[string match ".sym*" $child]} {
      set end_flag 0 ; break
  }}
  if $end_flag {exit}

# If last visible widget is gone from ., put a new widget in, or withdraw .
  if {([llength [pack slaves .]] == 0)} {
    set frame_flag 1
    foreach child [winfo children .] {
      if {[string match ".sym*" $child] && ([winfo class $child] == "Frame")} {
        pack $child -side top -expand yes -fill both
        focus $child.t
        set frame_flag 0
        break
    }}
    if $frame_flag { wm withdraw .
  }} else {focus "[lindex [pack slaves .] 0].t"}
}


# Set configs and fork off text windows for each file.

# Reads command line arguments.
proc els_parse_configs {argc argv} {
  if {([lsearch $argv "-a"] < 0) && $argc} {
    set interps [winfo interps]
    set my_place [lsearch $interps [winfo name .]]
    set others [lreplace $interps $my_place $my_place]
    set my_name [lindex [winfo name .] 0]
    set interp [lindex $others [lsearch -glob $others "[set my_name]*"]]
    if {![catch "send $interp info procs els_parse_configs" result]} {
      if {$result != ""} {
        send $interp cd [pwd]
        catch {send $interp after 1 \{els_parse_configs $argc \{$argv\}\}}
        exit
  }}}

  if {([lsearch $argv -H] >= 0) || ($argc == 0)} {
    global Els_Help
    puts $Els_Help
  }

  set option_flag 0
  set i 0
  set configs ""
  while {$i < $argc} {
    set item [lindex $argv $i]
    if {[string match {-[twc]} $item]} {
      incr i
      lappend configs $item [lindex $argv $i]
    } elseif {[string match -? $item]} {
      lappend configs $item
    } else {els_process_item $configs $item ; set option_flag 1}
    incr i
  }
  if {!$option_flag} {exit}
}

proc els_process_item {configs option} {
  global Els_Title_Comment
  set Els_Title_Comment [eval concat [els_return_option_assocs "-t" $configs]]
  if {([lsearch $configs "-p"] >= 0)} {set pile 1} else {set pile 0}

  global Els_Configs Els_Command
  set Els_Configs [eval concat [els_return_option_assocs "-w" $configs]]
  set Els_Command [join [els_return_option_assocs "-c" $configs] " ; "]

  set tl [els_process_option $option $pile]
  if {![winfo exists $tl]} {return}

  if {([lsearch $configs -i] < 0)} {
    if $pile {
      wm deiconify .
      pack $tl -side top -expand yes -fill both
    } else {wm deiconify $tl}
  } elseif {$pile && ([llength [pack slaves .]] == 0)} {
      pack $tl -side top -expand yes -fill both
      wm deiconify . ; wm iconify .
}}

# Updates t's toplevel window title (if it isn't in .). If name is not given,
# defaults to TH(File,$t)
proc th_file_update_titles {t {name ""}} {
  global TH
  if {$name == ""} {catch {set name $TH(File,$t)}}
  if {![catch {lindex $TH(File,Browse,Command) 0} cmd]} {
    if {[set i [string first "|" $name]] >= 0} {
      if {[string range $name $i [expr $i + [string length $cmd] -1]] == $cmd} {
        set name "[file tail [file dirname $name]]/"
  }}}
  set tl [winfo parent $t]
  if {[string first [string index $name 0] {~/}] >= 0} {
    if {[set pipe [string first "|" $name]] >= 0} {
      set title [string range $name $pipe end]
    } else {set title [file tail $name]
  }} else {set title $name}
  global Els_Title_Comment TH
  if {[catch "set TH(Busy,$t)"]} {set TH(Busy,$t) 0}
  if {([winfo class $tl] == "Frame") || ($tl == ".")} {return}
  set title_app "[string toupper [string index [tk appname] 0]][string range [lindex [tk appname] 0] 1 end]:"

# Now build title & iconname.
  set new_title "" ; set new_iconname ""

  if $TH(Busy,$tl.t) {
    append new_title "*BUSY* "
    append new_iconname "* "
  }

  if {[llength [wm title $tl]] == 1} { 	# new window
    set title_comment $Els_Title_Comment
  } else { # figure out old title comment
      if {[lindex [wm title $tl] 0] == "*BUSY*"} {set i 1} else {set i 0}
      if {[set ta [lsearch [wm title $tl] $title_app]] == $i} {
        set title_comment ""
      } else {set title_comment [lrange [wm title $tl] $i [expr $ta - 1]]}
  }
  if {$title_comment != ""} {
    append new_title "$title_comment "
    append new_iconname "$title_comment "
  }

  append new_title "$title_app $title"
  append new_iconname "[string index $title_app 0] $title"

  wm title $tl $new_title
  wm iconname $tl $new_iconname
}

proc els_return_option_assocs {option list} {
  set result ""
  set append_flag 0
  foreach item $list {
    if $append_flag {set result [lappend result $item]}
    set append_flag 0
    if {$item == $option} {set append_flag 1}
  }
  return $result
}

# Create new text window with option. Return toplevel window.
# Toplevel window also gets titled appropriatly, and sets the File() array.
proc els_process_option {option pile} {
  global TH
  set tl [els_new_text $pile]
  set TH(Pipe) 0
  set TH(Gradual) 0

  if {![catch {set TH(Busy,$tl.t)} result] && $result} {
    set busy 1 ; set TH(Busy,$tl.t) 0
  } else {set busy 0}

  if {($option == "0")} {
    set TH(File,$tl.t) ""
    th_file_update_titles $tl.t 0
  } elseif {($option == "X")} {
    set TH(File,$tl.t) ""
    th_file_update_titles $tl.t X
    if {[catch {$tl.t insert insert "[selection get]"}]} {}
    $tl.t mark set insert 1.0
  } elseif {($option == "-")} {
    set TH(File,$tl.t) ""
    th_file_update_titles $tl.t -
    after 0 th_Text_read_file $tl.t stdin 1
  } else {if {([string first [string index $option 0] {/~}] >= 0)} {
      set TH(File,$tl.t) $option
    } else {set TH(File,$tl.t) [pwd]/$option}
    if {[string match "*/|*" $TH(File,$tl.t)]} {
      after 1000 els_load_file $tl.t
    } elseif {(![file exists $TH(File,$tl.t)] ||
	![file readable $TH(File,$tl.t)])} {
      th_file_update_titles $tl.t
      if {[info procs th_file_update_widgets] != ""} {
        th_file_update_widgets $tl.t}
      if {[info procs th_flash_label] != ""} {
        th_flash_label $tl.t -text "No File: $TH(File,$tl.t)"
      }
    } else {els_load_file $tl.t
  }}
  if $busy {set TH(Busy,$tl.t) 0}

  if {[info procs elsbeth_new_text] != ""} {elsbeth_new_text $tl}
  return $tl
}


wm withdraw .
wm title . "Els"
wm iconname . "Els"
if {[info procs elsbeth_new_text] == ""} {
# hack for quitting els windows...overrided by Elsbeth.
  bind Text <Meta-q> "catch {els_destroy_text %W}"
}
if {([info procs th_source_local_files] != "") || \
       ([lsearch [array names auto_index] th_source_local_files] >= 0)} {
  th_source_local_files elsbeth
}
els_parse_configs $argc $argv
