#!/usr/local/bin/wish4.0
#
# jbrowser - Tk/Tcl-based directory browser
#
# Copyright 1992-1995 by Jay Sekora.  This file may be freely distributed,
# modified or unmodified, for any purpose, provided that this copyright
# notice is retained verbatim in all copies and no attempt is made to
# obscure the authorship of this file.  If you distribute any modified
# versions, I ask, but do not require, that you clearly mark any changes
# you make as such and that you provide your users with instructions for
# getting the original sources.

## begin boiler_header

if {[info exists env(JSTOOLS_LIB)]} {
  set jstools_library $env(JSTOOLS_LIB)
} else {
  set jstools_library /usr/local/lib/jstools
}

# add the jstools library to the library search path:

set auto_path [concat [list $jstools_library] $auto_path]

# check for ~/.tk and prepend it to the auto_path if it exists.
# that way the user can override and customise the jstools libraries.

if {[file isdirectory ~/.tk]} then {
  set auto_path [concat [list [glob ~/.tk]] $auto_path]
}

## end boiler_header

# TO DO:
#
# implement paul's idea of a `storage area' for files
#
# `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:configure_browser

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

proc jbr:init {} {
  j:jstools_init			;# prefs, libraries, bindings...
}

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

proc jbr:userinit {} {
  global J_PREFS			;# user preferences
  global BROWSERPREFS			;# user preferences
  global NAME
  global HOME
  
  # read in user's .tk/jbrowserrc.tcl and .tk/jbrowser-defaults files
  #
  j:source_config jbrowserrc.tcl
  j:read_prefs -array BROWSERPREFS -file jbrowser-defaults {
    {fancy 0}
    {lbwidth 20}
    {lbheight 20}
    {lbfont default}
  }
}

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

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

  if {$J_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} -underline 0 -menu .menu.browser.m
  menu .menu.browser.m
  .menu.browser.m add command -label {Help} \
    -underline 0 \
    -accelerator {[h]} \
    -command {jbr:cmd:help}
  .menu.browser.m add command -label {About the Browser . . .} \
    -underline 0 \
    -command {jbr:cmd:about}
  .menu.browser.m add command -label {Global Preferences . . .} \
    -underline 0 \
    -accelerator {[G]} \
    -command {j:global_pref_panel; jbr:apply_prefs}
  .menu.browser.m add command -label {Browser Preferences . . .} \
    -underline 0 \
    -accelerator {[P]} \
    -command {jbr:cmd:browser_prefs; jbr:apply_prefs}
  .menu.browser.m add separator
  .menu.browser.m add command -label {Up} \
    -underline 0 \
    -command {jbr:cd ..}
  .menu.browser.m add command -label $HOME \
    -command "cd $HOME; jbr:fill_browser"
  .menu.browser.m add command -label {/} \
    -underline 0 \
    -command {jbr:cd /}
  .menu.browser.m add command -label {Change Directory . . .} \
    -underline 7 \
    -accelerator {Tab} \
    -command {jbr:cmd:cd}
  .menu.browser.m add separator
  .menu.browser.m add command -label {Issue Tcl Command . . .} \
    -underline 6 \
    -accelerator {[T]} -command {j:prompt_tcl}
  .menu.browser.m add command -label {Issue Unix Command . . .} \
    -underline 6 \
    -accelerator {[U]} -command {j:prompt_unix}
  .menu.browser.m add separator
  .menu.browser.m add command -label {New Browser} \
    -underline 0 \
    -accelerator {[b]} \
    -command {exec jbrowser &}
  .menu.browser.m add command -label {Refresh} \
    -underline 0 \
    -accelerator {^l} \
    -command "jbr:fill_browser"
  .menu.browser.m add command -label {Quit . . .} \
    -underline 0 \
    -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} \
    -underline 0 -menu .menu.file.m
  menu .menu.file.m
  .menu.file.m add command -label {View} \
    -underline 0 -accelerator {CR} \
    -command jbr:cmd:view
  .menu.file.m add command -label {Edit} \
    -underline 0 -accelerator {[e]} \
    -command jbr:cmd:edit
  .menu.file.m add command -label {Process} \
    -underline 4 -accelerator {[u]} \
    -command jbr:cmd:process
  .menu.file.m add command -label {Print} \
    -underline 0 -accelerator {[p]} \
    -command jbr:cmd:print
  .menu.file.m add separator
  .menu.file.m add command -label {Create Directory . . .} \
    -underline 4 -accelerator {[n]} \
    -command jbr:cmd:newdir
  .menu.file.m add command -label {Move . . .} \
    -underline 0 -accelerator {[m]} \
    -command jbr:cmd:move
  .menu.file.m add command -label {Rename . . .} \
    -underline 4 -accelerator {[M]} \
    -command jbr:cmd:rename
  .menu.file.m add command -label {Duplicate} \
    -underline 0 -accelerator {[d]} \
    -command jbr:cmd:duplicate
  .menu.file.m add command -label {Destroy} \
    -underline 4 -accelerator {[r]} \
    -command jbr:cmd:destroy
  .menu.file.m add command -label {Get Info} \
    -underline 4 -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 J_PREFS
  if {[lsearch [array names J_PREFS] {scrollbarside}] == -1} {
    set J_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 -command ".cur.list yview"
  listbox .cur.list \
    -borderwidth 2 \
    -relief sunken \
    -yscroll ".cur.scroll set"
  
  j:tk3 {
    .cur.list configure -geometry 20x20
  }
  j:tk4 {
    .cur.list configure -width 20 -height 20 -selectmode extended
  }
  
  pack .cur -in . -side right -expand yes -fill both
  pack .cur.label -in .cur -side top -fill x
  pack .cur.scroll -in .cur \
    -side $J_PREFS(scrollbarside) -fill y
  pack .cur.list -in .cur -side $J_PREFS(scrollbarside) -fill both -expand yes
  
  
  wm minsize . 100 100
  wm maxsize . [winfo screenwidth .] [winfo screenheight .]
  
  focus .
  catch {focus default .}		;# caught for Tk 4.0
}

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

proc jbr:mkbindings {} {
  global env HOME USER
  
  j:tk3 {
    # 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-G>		{j:global_pref_panel; jbr:apply_prefs}
  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 . <Meta-P>		{jbr:cmd:browser_prefs; jbr:apply_prefs}
  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 J_PREFS BROWSERPREFS
  global filelist
  lappend filelist $file

  if $BROWSERPREFS(fancy) {
    if [catch {				;# in case of eg bad symlinks
      # 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]]
      if {[info commands fmtclock] != ""} {
        append fentry [fmtclock [file mtime ./$file] { %b %d %H:%M } ]
      }
      append fentry "  "
    }] {				;# couldn't stat some file, probably
      set fentry "????????????  "
    }
  }
  append fentry "$file"
  catch {				;# in case of bogus symlinks, etc.
    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):
  j:tk3 {
    set oldyview [lindex [.cur.scroll get] 2]
  }
  j:tk4 {
    set oldpos [.cur.list index @0,0]
  }

  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):
  j:tk3 {
    .cur.list yview $oldyview
  }
  j:tk4 {
    .cur.list yview $oldpos
  }
}

######################################################################
# 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 jdoc jbrowser &
}

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

proc jbr:cmd:about {} {
  global JSTOOLS_VERSION
  set about_browser [format {
    j:rt:hl "jbrowser"
    j:rt:cr
    j:rt:rm "by Jay Sekora, "
    j:rt:tt "js@calumet.org"
    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-1995 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."
  } $JSTOOLS_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 J_PREFS BROWSERPREFS env tk_strictMotif

  toplevel .bprefs
  wm title .bprefs "Browser Preferences"

  frame .bprefs.fancy
  checkbutton .bprefs.fancy.cb -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 -width 50 \
    -textvariable BROWSERPREFS(lbfont)
  frame .bprefs.size
  label .bprefs.size.wl -text {Width:}
  entry .bprefs.size.we -width 5 \
    -textvariable BROWSERPREFS(lbwidth)
  label .bprefs.size.hl -text {Height:}
  entry .bprefs.size.he -width 5 \
    -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}
        j:tk3 {
          .cur.list configure -geometry \
            [format {%sx%s} $BROWSERPREFS(lbwidth) $BROWSERPREFS(lbheight)]
        }
        j:tk4 {
          .cur.list configure \
            -width $BROWSERPREFS(lbwidth) \
            -height $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 <Key-Return> {.bprefs.b.save invoke}
  j:tk3 {
    bind .bprefs.font.bot.e <Key-Tab> {focus .bprefs.size.we}
    bind .bprefs.size.we <Key-Tab> {focus .bprefs.size.he}
    bind .bprefs <Key-Tab> {focus .bprefs.size.we}
  }
  j:tk4 {
    bind .bprefs.font.bot.e <Key-Return> +break
    bind .bprefs.size.we <Key-Return> +break
    bind .bprefs.size.he <Key-Return> +break
    bind .bprefs <Key-Return> +break
  }
  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 J_PREFS
  foreach i [getfiles] {
    if {![file isdirectory $i]} {
      jbr:attempt "lpr -P$J_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 {

*.jdoc		{exec jdoc [pwd]/$i}
*.dvi		{j:alert -text "I don't know how to view .dvi files."}
*.Z		{jbr:more {Uncompressed file} [jbr:attempt "zcat \"$i\""]}
*.tar.Z		{jbr:more {Contents of uncompressed tar file} \
		  [jbr:attempt "uncompress -c \"$i\" | tar -tvf -"]}
*.tar		{jbr:more {Contents of tar file} \
		  [jbr:attempt "tar -tvf \"$i\""]}
{*.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 J_PREFS
  set env(PRINTER) $J_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









