#!/usr/local/bin/wish -f
#
# tkbrowser - Tk/Tcl-based directory browser

global VERSION
set VERSION {3.6/2.0}

# Copyright (c) 1992-1994 by Jay Sekora.  All rights reserved, except
# that this file may be freely redistributed without fee for non-profit,
# noncommercial use.

# This script generates a directory browser, which lists the working
# directory and allows you to view files or subdirectories by
# double-clicking.  It also lets you perform a number of other kinds
# of operations on files and directories, and is customisable, so the
# user can tailor it for the kinds of files and operations sie works
# with.  Genesis was in the "browse" example from the Tk source
# distribution; modelled a bit after the NeXT browser.  (It also
# turned out looking a bit like the browser in the Point editor.)
# 
# usage:
#   jbrowser - start browsing in the current directory
# or
#   jbrowser <directory> - start browsing in the specified directory
#
# author
#   Jay Sekora <js@it.bu.edu>, July 1992--February 1993

# TO DO:
#
# implement paul's idea of a `storage area' for files
# document changes (tkb_alert -> j:alert, etc.)
#
# fix arrow keys so the selection stays visible
# `fast' pref to skip directory check (/)
# use the right mouse button in the browser for something.
# make the information display more informative and prettier.
# add a parent-directory column

######################################################################
# jbr:init - basic initialisation
######################################################################

proc jbr:init {} {
  global PREFS				;# user preferences
  global BROWSERPREFS			;# user preferences
  
  catch {tk colormodel . color}		;# colour even on a 2-bit display
  
  global tk_library
  global env
  
  global NAME; set NAME $env(USER)
  global HOME; set HOME $env(HOME)
  
  # check for $HOME/.tk/jlibrary.tcl and read it in if it exists.
  # this contains library procedures.  it would normally be in $tk_library,
  # but we check for its presence here for people who can't put things in
  # $tk_library.
  #
  if {[file isfile "$HOME/.tk/jlibrary.tcl"]} then {
    source "$HOME/.tk/jlibrary.tcl"
  }
  # now check for some other library files which might be there (again,
  # these would normally be in $tk_library):
  j:source_config jrichtext.tcl
  j:source_config jabout.tcl
  j:source_config jbindings.tcl
  
  # read in user's text bindings (mainly for Entry widgets):
  j:source_config -directory $HOME .textbindings.tcl
}

######################################################################
# jbr:userinit - user customisation
######################################################################

proc jbr:userinit {} {
  global PREFS				;# user preferences
  global BROWSERPREFS			;# user preferences
  global NAME
  global HOME
  
  # read in user's .tk/jbrowserrc.tcl and .tk/jbrowserprefs.tcl files
  #
  j:source_config jbrowserrc.tcl
  j:source_config jbrowserprefs.tcl
  j:read_standard_prefs
  j:read_prefs -array BROWSERPREFS -file jbrowser-defaults {
    {fancy 0}
    {lbwidth 20}
    {lbheight 20}
    {lbfont default}
  }
  
  # read in user's text bindings (mainly for Entry widgets):
  j:source_config -directory $HOME .textbindings.tcl
}

######################################################################
# jbr:apply_prefs - apply preferences to UI elements
######################################################################

proc jbr:apply_prefs {} {
  global PREFS				;# user preferences
  global BROWSERPREFS			;# user preferences
  global NAME
  global HOME
  global tk_strictMotif
  
  .cur.list configure -geometry \
    [format {%sx%s} $BROWSERPREFS(lbwidth) $BROWSERPREFS(lbheight)]
  j:configure_font .cur.list $BROWSERPREFS(lbfont);# knows about `default'
  
  switch -exact $PREFS(bindings) {
    basic {
      j:eb:basic_bind Entry
      j:tb:basic_bind Text
    }
    emacs {
      j:eb:emacs_bind Entry
      j:tb:emacs_bind Text
    }
  }

  if {$PREFS(tk_strictMotif)} {
    set tk_strictMotif 1
  } else {
    set tk_strictMotif 0
  }
}

######################################################################
# jbr:mkmenu - make menu bar
######################################################################

proc jbr:mkmenu {} {
  frame .menu -borderwidth 2 -relief raised
  jbr:mkmenu:browser
  jbr:mkmenu:file

  pack .menu.browser .menu.file -in .menu -side left
  pack .menu -in . -side top -fill x
  
  tk_menuBar .menu .menu.browser .menu.file
  tk_bindForTraversal .
}

######################################################################
# jbr:mkmenu:browser - make browser menu
######################################################################

proc jbr:mkmenu:browser {} {
  global env HOME USER
  
  menubutton .menu.browser -text {Browser} -menu .menu.browser.m
  menu .menu.browser.m
  .menu.browser.m add command -label {Help} -accelerator {[h]} \
    -command {jbr:cmd:help}
  .menu.browser.m add command -label {About the Browser . . .} \
    -command {jbr:cmd:about}
  .menu.browser.m add command -label {Global Preferences . . .} \
    -command {j:global_pref_panel; jbr:apply_prefs}
  .menu.browser.m add command -label {Browser Preferences . . .} \
    -command {jbr:cmd:browser_prefs; jbr:apply_prefs}
  .menu.browser.m add separator
  .menu.browser.m add command -label {Up} \
    -command {jbr:cd ..}
  .menu.browser.m add command -label $HOME \
    -command "cd $HOME; jbr:fill_browser"
  .menu.browser.m add command -label {/} \
    -command {jbr:cd /}
  .menu.browser.m add command -label {Change Directory . . .} \
    -accelerator {Tab} \
    -command {jbr:cmd:cd}
  .menu.browser.m add separator
  .menu.browser.m add command -label {Issue Tcl Command . . .} \
    -accelerator {[T]} -command {j:prompt_tcl}
  .menu.browser.m add command -label {Issue Unix Command . . .} \
    -accelerator {[U]} -command {j:prompt_unix}
  .menu.browser.m add separator
  .menu.browser.m add command -label {New Browser} -accelerator {[b]} \
    -command {exec jbrowser &}
  .menu.browser.m add command -label {Refresh} -accelerator {^l} \
    -command "jbr:fill_browser"
  .menu.browser.m add command -label {Quit . . .} -accelerator {[q]} \
    -command jbr:cmd:quit
}

######################################################################
# jbr:mkmenu:file - make file menu
######################################################################

proc jbr:mkmenu:file {} {
  global env HOME USER
  menubutton .menu.file -text {File} -menu .menu.file.m
  menu .menu.file.m
  .menu.file.m add command -label {View} -accelerator {CR} \
    -command jbr:cmd:view
  .menu.file.m add command -label {Edit} -accelerator {[e]} \
    -command jbr:cmd:edit
  .menu.file.m add command -label {Process} -accelerator {[u]} \
    -command jbr:cmd:process
  .menu.file.m add command -label {Print} -accelerator {[p]} \
    -command jbr:cmd:print
  .menu.file.m add separator
  .menu.file.m add command -label {Create Directory . . .} -accelerator {[n]} \
    -command jbr:cmd:newdir
  .menu.file.m add command -label {Move . . .} -accelerator {[m]} \
    -command jbr:cmd:move
  .menu.file.m add command -label {Rename . . .} -accelerator {[M]} \
    -command jbr:cmd:rename
  .menu.file.m add command -label {Duplicate} -accelerator {[d]} \
    -command jbr:cmd:duplicate
  .menu.file.m add command -label {Destroy} -accelerator {[r]} \
    -command jbr:cmd:destroy
  .menu.file.m add command -label {Get Info} -accelerator {[i]} \
    -command jbr:cmd:info
  .menu.file.m add cascade -label {Misc.} -accelerator {  >} \
    -menu .menu.file.m.misc
  
  #####
  # .menu.file.m add command -label {Execute} -command jbr:cmd:execute
  # .menu.file.m add command -label {Execute in xterm} \
  #   -command jbr:cmd:xterm_execute
  #####
  
  menu .menu.file.m.misc
  .menu.file.m.misc add command -label {TeX File} -accelerator {[t]} \
    -command jbr:cmd:tex
  .menu.file.m.misc add command -label {LaTeX File} -accelerator {[l]} \
    -command jbr:cmd:latex
  .menu.file.m.misc add command -label {Compress File} \
    -command jbr:cmd:compress
  .menu.file.m.misc add command -label {Tar Directory} \
    -command jbr:cmd:maketar
  .menu.file.m.misc add command -label {Run `make' in This Directory} \
    -command jbr:cmd:make
  .menu.file.m.misc add command -label {Edit with `jedit'} \
    -command jbr:cmd:jedit
  .menu.file.m.misc add command -label {Edit with `xedit'} \
    -command jbr:cmd:xedit
  .menu.file.m.misc add command -label {Print with `lpr'} \
    -command jbr:cmd:lpr
}

######################################################################
# jbr:mkmain - make main body of window (listbox, scrollbar)
######################################################################

proc jbr:mkmain {} {
  global env HOME USER
  global PREFS
  if {[lsearch [array names PREFS] {scrollbarside}] == -1} {
    set PREFS(scrollbarside) right ;# make sure it's defined
  }
    
  # create a frame

  frame .cur
  
  # create a label for the top of the column
  # create a listbox in the column
  # create a scrollbar for the listbox
  
  # actually, the exact value of -width doesn't matter, as long as it doesn't
  # make the label bigger than the listbox+scrollbar, because the label will
  # expand to the size of the parent window.  but specifying -width prevents
  # the label from expanding larger than the parent window and making the
  # whole top level window resize itself.
  
  label .cur.label -anchor center -width 10 -relief flat
  scrollbar .cur.scroll -relief flat -command ".cur.list yview"
  listbox .cur.list -borderwidth 0 -geometry 20x20 \
    -yscroll ".cur.scroll set"
  
  pack .cur -in . -side right -expand yes -fill both
  pack .cur.label [j:rule .cur] -in .cur -side top -fill x
  pack .cur.scroll [j:rule .cur] -in .cur \
    -side $PREFS(scrollbarside) -fill y
  pack .cur.list -in .cur -side $PREFS(scrollbarside) -fill both -expand yes
  
  
  wm minsize . 100 100
  wm maxsize . [winfo screenwidth .] [winfo screenheight .]
  
  focus .
  focus default .			;# . will get focus when dialogs exit
}

######################################################################
# jbr:mkbindings - make keyboard equivalents for commands
######################################################################

proc jbr:mkbindings {} {
  global env HOME USER

  # change Listbox binding so you need to shift to select multiple items
  bind Listbox <1> {%W select from [%W nearest %y]}
  bind Listbox <B1-Motion> {%W select from [%W nearest %y]}
  bind Listbox <Shift-1> {%W select from [%W nearest %y]}
  bind Listbox <Shift-B1-Motion> {%W select to [%W nearest %y]}
  
  bind .cur.list <Double-Button-1> {jbr:browse}
  
  bind . <Up>			{jbr:moveup}
  bind . <Down>			{jbr:movedown}
  bind . <Left>			{jbr:cd ..}
  bind . <Right>		{jbr:browse}
  bind . <Return>		{jbr:browse}
  bind . <Tab>			{jbr:cmd:cd}
  bind . <period><period>	{jbr:cmd:cd ..}
  bind . <Meta-b>		{exec jbrowser &}
  bind . <Meta-d>		{jbr:cmd:duplicate}
  bind . <Meta-e>		{jbr:cmd:edit}
  bind . <Meta-h>		{jbr:cmd:help}
  bind . <Meta-i>		{jbr:cmd:info}
  bind . <Control-l>		{jbr:fill_browser}
  bind . <Meta-l>		{jbr:cmd:latex}
  bind . <Meta-m>		{jbr:cmd:move}
  bind . <Meta-Key-M>		{jbr:cmd:rename}
  bind . <Meta-n>		{jbr:cmd:newdir}
  bind . <Meta-p>		{jbr:cmd:print}
  bind . <Any-q>		{jbr:cmd:quit}
  bind . <Meta-r>		{jbr:cmd:destroy}
  bind . <Meta-t>		{jbr:cmd:tex}
  bind . <Meta-T>		{j:prompt_tcl}
  bind . <Meta-u>		{jbr:cmd:process}	;# mnemonic: use
  bind . <Meta-U>		{j:prompt_unix}
}

######################################################################
# jbr:attempt - attempt a Unix command and report an error on failure
######################################################################
# really ought to display output only if it's non-null
#
proc jbr:attempt { cmd } {
  if [catch [format "exec %s" $cmd] output] then {
    if [expr {[string length $output] < 80}] then {
      j:alert -text \
        "The command\n    $cmd\n failed with the following output:\n\n$output"
    } else {
      jbr:more {Output of failed command} \
        "The command\n\t$cmd\nfailed with the following output:\n\n$output"
    }
  }
  return $output
}

######################################################################
# jbr:addfile - adds a file name to the list
#   procedure by Paul Raines <raines@bohr.physics.upenn.edu>
######################################################################

proc jbr:addfile {file} {
  global PREFS BROWSERPREFS
  global filelist
  lappend filelist $file

  if $BROWSERPREFS(fancy) {
    # more efficient to use file stat to get everything at once
    case [file type $file] {
      directory {set fentry "d"}
      blockSpecial {set fentry "b"}
      characterSpecial {set fentry "c"}
      link {set fentry "l"}
      fifo {set fentry "p"}
      socket {set fentry "s"}
      default {set fentry "-"}
    }
    if [file readable $file] {append fentry "r"} {append fentry "-"}
    if [file writable $file] {append fentry "w"} {append fentry "-"}
    if [file executable $file] {append fentry "x"} {append fentry "-"}

    append fentry [format {%8d} [file size $file]]
###    append fentry [fmtclock [file mtime $file] { %b %d %H:%M } ]
    append fentry "  "
  }
  append fentry "$file"
  if [file isdirectory $file] {
    append fentry "/"
  } else {
    if [file executable $file] {append fentry "*"}
  }
  .cur.list insert end $fentry
}

######################################################################
# getfiles - gets a file name(s) from the list
#   procedure by Paul Raines <raines@bohr.physics.upenn.edu>
######################################################################

proc getfiles {} {
    set retlist ""
    set ndcs [.cur.list curselection]
    if { $ndcs == "" }  { 
	return $retlist 
    } else {
	global filelist
	foreach i $ndcs {
	    lappend retlist [lindex $filelist $i]
	}
	return $retlist
    }
}

######################################################################
# jbr:fill_browser - this loads the current directory into the browser
#   modified by Paul Raines <raines@bohr.physics.upenn.edu>
#   note: if you need to reposition the scrollbar, do it explicitly
######################################################################

proc jbr:fill_browser {} {
  global dmtime
  set dmtime [file mtime .]
  global filelist
  set filelist ""

  # save current scroll value (to prevent jumping to top):
  set oldyview [lindex [.cur.scroll get] 2]

  set curr [file tail [pwd]]
  if {"x$curr" == "x"} then {set curr "/"}
  
  .cur.label configure -text $curr
  wm iconname . $curr

  # fill in contents of the current directory:
  .cur.list delete 0 end

  # add ".." to go up a level:
  jbr:addfile ".."

  # add all normal (non-dot) files:
  foreach i [lsort [glob -nocomplain *]] {
    jbr:addfile $i
  }

  # add any dot-files:
  foreach i [lsort [glob -nocomplain .*]] {
    if {$i != "." && $i != ".."} {
      jbr:addfile $i
    }
  }
  # restore old scroll value (to prevent jumping to top):
  .cur.list yview $oldyview
}

######################################################################
# jbr:update_browser - call jbr:fill_browser if necessary (if dir modified)
######################################################################

proc jbr:update_browser {} {
  global dmtime
  if { $dmtime != [file mtime .] } {jbr:fill_browser}
}

######################################################################
# jbr:cd dir - change directory, update label, icon, scrollbar, list
######################################################################

proc jbr:cd {dir} {
  cd $dir
  jbr:fill_browser
  .cur.list yview 0
}

######################################################################
# routines for moving the selection
######################################################################

### jbr:moveup - move the selection up

proc jbr:moveup {} {
  set bottom [.cur.list size]
  set selpos $bottom

  set selected [.cur.list curselection]
  if {[llength $selected] > 0} {
    set selpos [lindex $selected 0]
  }
  if {$selpos > 0} {
    set selpos [expr {$selpos - 1}]
  }
  .cur.list select clear
  .cur.list select from $selpos
  
  set rows [lindex [.cur.scroll get] 1]
  set lastvisible [lindex [.cur.scroll get] 3]
  set firstvisible [lindex [.cur.scroll get] 2]
  if {$firstvisible > $selpos} {
    .cur.list yview $selpos
  }
  if {$lastvisible < $selpos} {
    .cur.list yview [expr {$selpos - $rows + 1}]
  }
}

### jbr:movedown - move the selection down

proc jbr:movedown {} {
  set bottom [.cur.list size]
  set selpos 0

  set selected [.cur.list curselection]
  if {[llength $selected] > 0} {
    set selpos [lindex $selected 0]
  }
  if {$selpos < $bottom} {
    set selpos [expr {$selpos + 1}]
  }
  .cur.list select clear
  .cur.list select from $selpos
  
  set rows [lindex [.cur.scroll get] 1]
  set lastvisible [lindex [.cur.scroll get] 3]
  set firstvisible [lindex [.cur.scroll get] 2]
  if {$firstvisible > $selpos} {
    .cur.list yview $selpos
  }
  if {$lastvisible < $selpos} {
    .cur.list yview [expr {$selpos - $rows + 1}]
  }
}

######################################################################
# jbr:browse - action of double-click; cd if directory, else view
######################################################################

proc jbr:browse {} {
  set filename [lindex [getfiles] 0]
  if [file isdirectory $filename] {
    jbr:cd $filename
  } else {
    jbr:cmd:view
  }
}

######################################################################
# jbr:more - j:more plus jbr:update_browser (deprecated)
######################################################################
# Note that it calls `jbr:update_browser'; this is convenient, as the output
#   is typically a Unix command in brackets, which may change the
#   contents of the current directory.

proc jbr:more { hdr output } {
  j:more -title $hdr -text $output
  jbr:update_browser
}


######################################################################
#
# COMMAND ROUTINES
#
######################################################################

######################################################################
# jbr:cmd:help - this displays the help text
######################################################################

proc jbr:cmd:help {} {
# error checking!
  exec jhelp jbrowser &
}

######################################################################
# jbr:cmd:about - make the about box
######################################################################

proc jbr:cmd:about {} {
  global VERSION
  set about_browser [format {
    j:rt:hl "jbrowser"
    j:rt:cr
    j:rt:rm "by Jay Sekora, "
    j:rt:tt "js@it.bu.edu"
    j:rt:par
    j:rt:rm "A customisable directory browser for X Windows."
    j:rt:cr
    j:rt:rm "Version %s."
    j:rt:par
    j:rt:rm "Copyright \251 1992-1994 by Jay Sekora.  "
    j:rt:rm "All rights reserved, except that this file may be freely "
    j:rt:rm "redistributed in whole or in part for non\255profit, "
    j:rt:rm "noncommercial use."
    j:rt:par
    j:rt:rm "If you find bugs or have suggestions for improvement, "
    j:rt:rm "please let me know.  "
    j:rt:rm "Feel free to use bits of this code in your own "
    j:rt:tt "wish"
    j:rt:rm " scripts."
  } $VERSION]
  j:about .about $about_browser
  j:about:button .about {About jbrowser} $about_browser
  j:about:button .about {About The Author} [j:about_jay]
  j:about:button .about {About Tk and Tcl} [j:about_tktcl]
  
  tkwait window .about
}

######################################################################
# jbr:cmd:quit - exit the script
######################################################################

proc jbr:cmd:quit {} {
  if [j:confirm -text {Are you sure you want to quit?}] then {
    destroy .
    exit
  }
}

######################################################################
# jbr:cmd:browser_prefs - preferences panel
######################################################################

proc jbr:cmd:browser_prefs {} {
  global PREFS BROWSERPREFS env tk_strictMotif

  toplevel .bprefs
  wm title .bprefs "Browser Preferences"

  frame .bprefs.fancy
  checkbutton .bprefs.fancy.cb -relief flat -anchor w \
    -text {Long file listings} -variable BROWSERPREFS(fancy)
  frame .bprefs.font
  frame .bprefs.font.top
  label .bprefs.font.top.l -text {Font:}
  button .bprefs.font.top.default -width 8 -text {Default} -command {
    set BROWSERPREFS(lbfont) {default}
  }
  button .bprefs.font.top.choose -text {Choose . . .} -command {
    set BROWSERPREFS(lbfont) [j:prompt_font]
  }
  frame .bprefs.font.bot
  entry .bprefs.font.bot.e -relief sunken -width 50 \
    -textvariable BROWSERPREFS(lbfont)
  frame .bprefs.size
  label .bprefs.size.wl -text {Width:}
  entry .bprefs.size.we -relief sunken -width 3 \
    -textvariable BROWSERPREFS(lbwidth)
  label .bprefs.size.hl -text {Height:}
  entry .bprefs.size.he -relief sunken -width 3 \
    -textvariable BROWSERPREFS(lbheight)

  j:buttonbar .bprefs.b -default save -buttons {
    {
      save Save {
        j:write_prefs -array BROWSERPREFS -file jbrowser-defaults
        .bprefs.b.done invoke
      }
    } {
      done Done {
        if {$BROWSERPREFS(lbwidth) < 10} {set BROWSERPREFS(lbwidth) 10}
        if {$BROWSERPREFS(lbheight) < 3} {set BROWSERPREFS(lbheight) 3}
        .cur.list configure -geometry \
          [format {%sx%s} $BROWSERPREFS(lbwidth) $BROWSERPREFS(lbheight)]
        j:configure_font .cur.list $BROWSERPREFS(lbfont)
        jbr:fill_browser
        destroy .bprefs
      }
    }
  }
  
  pack append .bprefs.fancy .bprefs.fancy.cb {left expand fillx}
  pack append .bprefs.font.top \
    .bprefs.font.top.l {left} \
    .bprefs.font.top.choose {right padx 10 pady 5} \
    .bprefs.font.top.default {right pady 5}
  pack append .bprefs.font.bot \
    .bprefs.font.bot.e {left padx 10 pady 5}
  pack append .bprefs.font \
    .bprefs.font.top {top expand fillx} \
    .bprefs.font.bot {top expand fillx}
  pack append .bprefs.size .bprefs.size.wl {left fillx}
  pack append .bprefs.size .bprefs.size.we {left}
  pack append .bprefs.size .bprefs.size.hl {left fillx}
  pack append .bprefs.size .bprefs.size.he {left}

  pack append .bprefs \
    .bprefs.fancy {top fill} \
    [j:rule .bprefs -width 200] {top fillx} \
    .bprefs.font {top fillx} \
    [j:rule .bprefs -width 200] {top fillx} \
    .bprefs.size {top expand fill} \
    [j:rule .bprefs -width 200] {top fillx} \
    .bprefs.b {top expand fillx}

  j:dialogue .bprefs		;# position in centre of screen

  focus .bprefs
  bind .bprefs.font.bot.e <Key-Return> {.bprefs.b.save invoke}
  bind .bprefs.size.we <Key-Return> {.bprefs.b.save invoke}
  bind .bprefs.size.he <Key-Return> {.bprefs.b.save invoke}
  bind .bprefs.font.bot.e <Key-Tab> {focus .bprefs.size.we}
  bind .bprefs.size.we <Key-Tab> {focus .bprefs.size.he}
  bind .bprefs.size.he <Key-Tab> {focus .bprefs.printer.e}
  bind .bprefs <Key-Return> {.bprefs.b.save invoke}
  bind .bprefs <Key-Tab> {focus .bprefs.size.we}
  grab .bprefs
  tkwait window .bprefs
}

######################################################################
# jbr:cmd:cd - let user type directory to cd to
######################################################################

proc jbr:cmd:cd {} {
  set dirname [j:fs -buttons {here cancel home root} \
    -prompt "Change directory to..."]
  if {$dirname != {}} then {
    if [file isdirectory $dirname] {
      jbr:cd $dirname
    } else {
      j:alert -text "Directory \"$dirname\" does not exist."
    }
  }
}

######################################################################
# jbr:cmd:newdir - make a new directory
######################################################################

proc jbr:cmd:newdir {} {
  set dirname [j:fs -prompt "Create a new directory named..."]
  if {$dirname != {}} then {
    jbr:attempt "mkdir \"$dirname\""
    jbr:update_browser
  }
}

######################################################################
# jbr:cmd:duplicate - make a copy of a file or directory
######################################################################

proc jbr:cmd:duplicate {} {
  foreach i [getfiles] {
    jbr:attempt "cp -r \"$i\" \"copy_of_$i\""
  }
  jbr:update_browser
}

######################################################################
# jbr:cmd:destroy - delete a file or directory
######################################################################
# PROBLEM: rm seems to return 0 even on permission failure

proc jbr:cmd:destroy {} {
  foreach i [getfiles] {
    if {[file isdirectory $i]} then {
      if [j:confirm -priority 75 -text \
      "Are you sure you want to destroy ``$i'' and all its contents?"] {
        jbr:attempt "rm -r \"$i\""
      }
    } else {
      if [j:confirm -priority 75 -text "Are you sure you want to destroy ``$i''?"] {
        jbr:attempt "rm \"$i\""
      }
    }
  }
  jbr:update_browser
}

######################################################################
# jbr:cmd:move - rename or move a file or directory
######################################################################
# NEED TO CHECK FOR SYMLINKS TOO!

proc jbr:cmd:move {} {
  set old_cwd [pwd]
  if {$old_cwd == "/"} {set old_cwd ""}

  foreach i [getfiles] {
    if {[file isdirectory $i]} then {set i [file dirname $i]}
    set j [j:fs -buttons {here cancel home root} \
      -prompt "Move `$i' to..."]

    if {$j == {}} then {return 0}

    if [j:confirm -text "Are you sure you want to move `$i' to `$j'?"] then {
      jbr:attempt "mv \"$old_cwd/$i\" \"$j\""
    }
  }
  jbr:update_browser
}

######################################################################
# jbr:cmd:rename - rename a file or directory
######################################################################
# NEED TO CHECK FOR SYMLINKS TOO!

proc jbr:cmd:rename {} {
  set old_cwd [pwd]
  if {$old_cwd == "/"} {set old_cwd ""}

  foreach i [getfiles] {
    if {[file isdirectory $i]} then {set i [file dirname $i]}
    set j [j:prompt -file 1 -text "Rename `$i' to..."]

    if {$j == {}} then {return 0}

    if [j:confirm -text "Are you sure you want to rename `$i' to `$j'?"] then {
      jbr:attempt "mv \"$old_cwd/$i\" \"$j\""
    }
  }
  jbr:update_browser
}

######################################################################
# jbr:cmd:execute - run an executable X application
######################################################################
#
# NOT CURRENTLY USED - the menu item is commented out

proc jbr:cmd:execute {} {
  foreach i [getfiles] {
    jbr:attempt "\"$i\" &"
  }
}

######################################################################
# jbr:cmd:xterm_execute - run a command in an xterm window
######################################################################
#
# NOT CURRENTLY USED - the menu item is commented out

proc jbr:cmd:xterm_execute {} {
  foreach i [getfiles] {
    jbr:attempt "xterm -e \"$i\" &"
  }
}

######################################################################
# jbr:cmd:tex - process a file with tex
#   this routine is needed because the .tex extension is ambiguous
#   between tex and latex.
######################################################################

proc jbr:cmd:tex {} {
  foreach i [getfiles] {
    if {![file isdirectory $i]} {
      jbr:attempt "tex \"$i\" < /dev/null"
    } else {
      set i [file dirname $i]
      j:alert -text "`$i' is a directory."
    }
  }
}

######################################################################
# jbr:cmd:latex - process a file with latex
#   this routine is needed because the .tex extension is ambiguous
#   between tex and latex.
######################################################################

proc jbr:cmd:latex {} {
  foreach i [getfiles] {
    if {![file isdirectory $i]} {
      jbr:attempt "latex \"$i\" < /dev/null"
    } else {
      set i [file dirname $i]
      j:alert -text "`$i' is a directory."
    }
  }
}

######################################################################
# jbr:cmd:make - run make in the current (NOT the selected) directory
######################################################################

proc jbr:cmd:make {} {
  jbr:more {Output of `make':} [jbr:attempt make]
}

######################################################################
# jbr:cmd:jedit - edit a file with jedit
#   this routine is needed in case you have something you _can_ view
#   or edit with another tool (say .eps with a drawing tool) but you
#   want to edit the actual ASCII text.
######################################################################
# can't use jbr:attempt with background execs

proc jbr:cmd:jedit {} {
  foreach i [getfiles] {
    if {![file isdirectory $i]} {
      exec jedit "$i" &
    } else {
      set i [file dirname $i]
      j:alert -text "`$i' is a directory."
    }
  }
}

######################################################################
# jbr:cmd:xedit - edit a file with xedit
#   this routine is needed in case you have something you _can_ view
#   or edit with another tool (say .eps with a drawing tool) but you
#   want to edit the actual ASCII text.
######################################################################
# can't use jbr:attempt with background execs

proc jbr:cmd:xedit {} {
  foreach i [getfiles] {
    if {![file isdirectory $i]} {
      exec xedit "$i" &
    } else {
      set i [file dirname $i]
      j:alert -text "`$i' is a directory."
    }
  }
}

######################################################################
# jbr:cmd:lpr - print a file with lpr
#   this routine is needed in case you have something you _can_
#   print with another tool (say tex source with a script that
#   processes it) and you want to print the actual source file.
######################################################################

proc jbr:cmd:lpr {} {
  global PREFS
  foreach i [getfiles] {
    if {![file isdirectory $i]} {
      jbr:attempt "lpr -P$PREFS(printer) \"$i\""
    } else {
      set i [file dirname $i]
      j:alert -text "`$i' is a directory."
    }
  }
}

######################################################################
# jbr:cmd:compress - compress a file
######################################################################

proc jbr:cmd:compress {} {
  foreach i [getfiles] {
    if {![file isdirectory $i]} {
      jbr:attempt "compress $i"
      jbr:update_browser
    } else {
      set i [file dirname $i]
      j:alert -text "`$i' is a directory."
    }
  }
}

######################################################################
# jbr:cmd:maketar - make a tar file of a directory
######################################################################

proc jbr:cmd:maketar {} {
  foreach i [getfiles] {
    if {[file isdirectory $i]} then {
      set i [file dirname $i]
      jbr:more "Output of `tar cvf $i':" [jbr:attempt "tar cvf \"$i.tar\" \"$i\""]
    } else {
      j:alert -text "`$i' is not a directory."
    }
  }
  jbr:update_browser
}

######################################################################
# jbr:cmd:info - get information about a file
######################################################################

proc jbr:cmd:info {} {
  foreach i [getfiles] {
    if {![file isdirectory $i]} {
      if {[regexp "text" [exec file $i]]} then {
        jbr:more "Information about file `$i'" \
          [format "\tls -l:\n\n%s\n\n\tfile:\n\n%s\n\n\thead:\n\n%s" \
          [exec ls -l $i] [exec file $i] [jbr:attempt "head -20 \"$i\""]]
      } else {
        jbr:more "Information about file `$i'" \
          [format "\tls -l:\n\n%s\n\n\tfile:\n\n%s" \
          [exec ls -l $i] [exec file $i]]
      }
    } else {
      jbr:more "Information about directory `$i'" \
        [format "\tls -ld:\n\n%s" \
        [exec ls -ld $i]]
    }
  }
}


######################################################################
#
# DEFAULT FILE-MANIPULATION PROCEDURES
#   these are minimal `file-manipulation' procedures.
#   the user can redefine them (as all procedures) by putting
#   new procedures in hir ~/.tk/jbrowserrc file
#
######################################################################

######################################################################
# jbr:cmd:view - view contents of a file
######################################################################

proc jbr:cmd:view {} {
  foreach i [getfiles] {
    if {![file isdirectory $i]} {
      case $i in {

*.dvi		{j:alert -text "I don't know how to view .dvi files."}
*.Z             {jbr:more {Uncompressed file} [jbr:attempt "zcat \"$i\""]}
*.tar		{j:alert -text "I don't know how to view tar files."}
{*.tiff *.gif *.jpeg *.jpg}
		{j:alert -text "I don't know how to deal with image files."}
default		{jbr:more "Contents of `$i'" [jbr:attempt "cat \"$i\""]}

      }
    } else {
      set i [file dirname $i]
      j:alert -text "`$i' is a directory."
    }
  }
}

######################################################################
# jbr:cmd:edit - edit a file
######################################################################

proc jbr:cmd:edit {} {
  foreach i [getfiles] {
    if {![file isdirectory $i]} {
      case $i in {
*.dvi		{j:alert -text "This is a binary file and can't be edited."}
{*.Z *.tar}	{j:alert -text \
		"This file can't be edited without further processing."}
{*.tiff *.gif *.jpeg *.jpg}
		{j:alert -text "I don't know how to deal with image files."}
default		{exec jedit $i &}

      }
    } else {
      set i [file dirname $i]
      j:alert -text "`$i' is a directory."
    }
  }
}

######################################################################
# jbr:cmd:process - compile or otherwise process a file
######################################################################

proc jbr:cmd:process {} {
  foreach i [getfiles] {
    if {![file isdirectory $i]} {
      case $i in {

[mM]akefile*	{jbr:more "Output of `make -f $i'" [jbr:attempt "make -f \"$i\""]}
*.tex		{jbr:more {Output of `tex'} \
                  [jbr:attempt "tex \"$i\" < /dev/null"]}
*.latex		{jbr:more {Output of `latex'} \
                  [jbr:attempt "latex \"$i\" < /dev/null"]}
*.Z		{jbr:attempt "uncompress \"$i\""; jbr:update_browser}
*.tar		{jbr:more "Output of `tar xvf $i'" [jbr:attempt "tar xvf \"$i\""]}
{*.troff *.tr}	{j:alert -text "I don't know how to process `troff' files."}
{*.tiff *.gif *.jpeg *.jpg}
		{j:alert -text "I don't know how to deal with image files."}
default		{j:alert -text "I don't know what to do with this file."}

      }
    } else {
      set i [file dirname $i]
      j:alert -text "`$i' is a directory."
    }
  }
}

######################################################################
# jbr:cmd:print - send a file to the printer
######################################################################

# NOTE: for tex files, assumes dvips is available

proc jbr:cmd:print {} {
  global env PREFS
  set env(PRINTER) $PREFS(printer)
  foreach i [getfiles] {
    if {![file isdirectory $i]} {
      case $i in {

*.dvi		{jbr:more {Output of `dvips'} [jbr:attempt "dvips \"$i\""]}
*.tex		{j:alert \
		-text "Process this file, and then print the .dvi file."}
*.latex		{j:alert \
		-text "Process this file, and then print the .dvi file."}
core		{j:alert -text \
		"You don't really want to print a core file, do you?"}
{*.Z *.tar}	{j:alert -text \
		"This file can't be printed without further processing."}
{*.tiff *.gif *.jpeg *.jpg}
		{j:alert -text "I don't know how to deal with image files."}
default		{jbr:attempt "lpr \"$i\""}

      }
    } else {
      set i [file dirname $i]
      j:alert -text "`$i' is a directory."
    }
  }
}


######################################################################
######################################################################
##########  END OF PROCEDURE DEFINITIONS  ############################
######################################################################
######################################################################


######################################################################
# FINAL SETUP
######################################################################

jbr:init
jbr:userinit

jbr:mkmenu
jbr:mkmain
jbr:mkbindings

if {[info procs jbr:userhook] == "jbr:userhook"} {
  jbr:userhook
}
jbr:apply_prefs

# process argument, if any
#
if {$argc > 0} {
  set dir [lindex $argv 0]
} else {
  set dir "."
}
jbr:cd $dir

# fill the browser for the first time
#
jbr:fill_browser
