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

# 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. Lis 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
        =       Fill window with standard input gradually
        file    Fill window with contents of file
        |cmd    Execute command, load output
        |cmd&   Execute command, load output gradually
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.


# For arbitrary symbol generation

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

# Procedures for filling the text widget.

# Loads w with contents of TH(File,$w)
# if insert_flag is given, then insert, otherwise load.
# (Similar to file.Misc.tcl's version, but allows for gradual reading, too.)
proc els_load_file {w {insert_flag 0}} {
  global TH
  if {[catch "set TH(Pipe)"]} {set TH(Pipe) 0}
  if {[catch "set TH(Gradual)"]} {set TH(Gradual) 0}

  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]
  } elseif $TH(Pipe) {
    cd [file dirname $TH(File,$w)]
    set name [file tail $TH(File,$w)]
  } else {  set name $TH(File,$w)}
  if {([string match \|* $name]) && \
	([string match *\& $name] || $TH(Gradual))} {
    set file [open [string trimright $name {& }] r]
    set graduated 1
  } else {set file [open $name r] ; set graduated 0}

  if {([string match \|* $name]) &&
      ([string match *\& $name] || $TH(Gradual))} {
    set file [open [string trimright $name {& }] r]
    set graduated 1
  } else {set file [open $name r]}

  if {![file writable [string trimright $TH(File,$w) {&}]] &&
	[file exists [string trimright $TH(File,$w) {&}]]} {
    if {[info procs th_flash_label] != ""} {
      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)}
    th_Text_insert_file $w $file $graduated
    set TH(Modified,$w) 1
  } else {
    els_update_window_title [winfo parent $w] $TH(File,$w)
    th_Text_read_file $w $file $graduated
    catch {set TH(Mtime,$w) [file mtime $TH(File,$w)]}
    set TH(Modified,$w) 0
    if {[info procs th_kill_undos] != ""} {th_kill_undos $w}
    if {[info procs th_file_update_widgets] != ""} {th_file_update_widgets $w}
    set TH(File,Last,$w) "r"
  }
  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 ".[th_gensym]"
  if {$pile} {
    frame $tl
    bind $tl <Leave> {+set Els_Focus(%W) [focus]}
    bind $tl <Enter> {+if {[catch {focus $Els_Focus(%W)}] || (![string match "%W*" $Els_Focus(%W)])} {focus %W.t}}
  } else {
    toplevel $tl
    wm iconify $tl
  }
  text $tl.t -relief sunken -setgrid true
  pack $tl.t -in $tl -side right -expand yes -fill both
  focus $tl.t

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

  return $tl
}

# Switches focus to a new window, of same class if possible.
proc els_focus_new {class} {
  set w(Frame) "" ; set w(Toplevel) ""
  foreach child [winfo children .] {
    if {[string match ".sym*" $child] && [winfo ismapped $child]} {
      set w([winfo class $child]) $child
  }}
  if {$w($class) != ""} {focus "$w($class).t"
  } elseif {$w(Frame) != ""} {focus "$w(Frame).t"
  } elseif {$w(Toplevel) != ""} {focus "$w(Toplevel).t"}
}

# 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 {destroy .}

  els_focus_new $class

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


# 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_name [lindex [winfo name .] 0]
    set interp [lindex $interps [lsearch -glob $interps "[set my_name]*"]]
    if {$interp != [winfo name .]} {
      if {![catch "send $interp info procs els_parse_configs" result]} {
        if {$result != ""} {
          send $interp cd [pwd]
          catch {send $interp 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 "[els_return_option_assocs "-t" $configs] "
  if {$Els_Title_Comment == " "} {set Els_Title_Comment ""}

  if {([lsearch $configs "-p"] >= 0)} {set pile 1} else {set pile 0}

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

  eval $tl.t configure [lindex [els_return_option_assocs "-w" $configs] 0]

  set cmd [els_return_option_assocs "-c" $configs]
  if {$cmd != ""} {
    uplevel #0 [lindex $cmd 0]
}}

proc els_update_window_title {tl name} {
  if {([string match /* $name])} {
    set title [file tail $name]
  } else {set title $name}
  global Els_Title_Comment App_Name TH Els_Titles
  set Els_Titles($tl) "[set Els_Title_Comment][set App_Name]: [set title]"
  set els_icontitle "[set Els_Title_Comment][string index $App_Name 0] [set title]"
  if {([winfo class $tl] == "Frame") || ($tl == ".")} {return}
  if $TH(Busy) {
    wm title $tl "*BUSY* $Els_Titles($tl)"
    wm iconname $tl "* $els_icontitle"
  } else {
    wm title $tl $Els_Titles($tl)
    wm iconname $tl $els_icontitle
}}

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 {($option == "0")} {
    set TH(File,$tl.t) ""
    els_update_window_title $tl 0
  } elseif {($option == "X")} {
    set TH(File,$tl.t) ""
    els_update_window_title $tl X
    if {[catch {$tl.t insert insert "[selection get]"}]} {}
    $tl.t mark set insert 1.0
  } elseif {($option == "-")} {
    set TH(File,$tl.t) ""
    els_update_window_title $tl -
    after 0 els_read_file $tl.t stdin
  } elseif {($option == "=")} {
    set TH(File,$tl.t) ""
    els_update_window_title $tl =
    after 0 els_read_file $tl.t stdin 1
  } else {if {([string match /* $option])} {
      set TH(File,$tl.t) $option
    } else {set TH(File,$tl.t) [pwd]/$option}
    if {(![file exists $TH(File,$tl.t)] || ![file readable $TH(File,$tl.t)]) &&
	![string match "*/|*" $TH(File,$tl.t)]} {
      els_update_window_title $tl [file tail $TH(File,$tl.t)]
      if {[info procs th_flash_label] != ""} {
        th_flash_label $tl.t -text "No File: $TH(File,$tl.t)"
      }
    } else {if {[string match "*&" $TH(File,$tl.t)]} {
         after 1000 els_load_file $tl.t
       } else {els_load_file $tl.t}
  }}

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

proc els_source_local_files {name} {
  global env
  if {[file exists $env(HOME)/.th/$name]} {
    source $env(HOME)/.th/$name
  }
  if {[file exists ".$name"] && ([pwd] != "$env(HOME)/.th")} {
    source ".$name"
}}


# Global stuff
if {[info globals App_Name] == ""} {
  global App_Name
  set App_Name [string toupper [string index [file tail [info script]] 0]]
  append App_Name [string tolower [string range [file tail [info script]] 1 end]]
}

wm withdraw .
wm title . "Els"
wm iconname . "Els"
if {[info procs elsbeth_new_text] == ""} {
  bind Text <Meta-q> "catch {els_destroy_text %W}"
}
bind Toplevel <Leave> {+set Els_Focus(%W) [focus]}
bind Toplevel <Enter> {+if {[catch {focus $Els_Focus(%W)}] || (![string match "%W*" $Els_Focus(%W)])} {focus %W.t}}
els_source_local_files [string tolower $App_Name]
els_parse_configs $argc $argv


