#
# TCL Library for tkcvs
#

#
# $Id: workdir.tcl,v 1.88 2002/01/21 08:19:58 dorothyr Exp $
#
# Current working directory display.  Handles all of the functions
# concerned with navigating about the current directory on the main
# window.
#

proc workdir_setup {} {
  global cwd
  global module_dir
  global cvscfg
  global cvsglb
  global current_tagname
  global feedback
  global logclass

  gen_log:log T "ENTER"
  set cwd [pwd]
  set pid [pid]

  if {[winfo exists .workdir]} {
    wm deiconify .workdir
    raise .workdir
    return
  }

  # Make a new toplevel and unmap . so that the working directory browser
  # the module browser are equal
  toplevel .workdir
  wm title .workdir "TkCVS"
  wm iconname .workdir "TkCVS"
  wm iconbitmap .workdir @$cvscfg(bitmapdir)/tkcvs48.xbm
  wm minsize .workdir 5 10
  wm protocol .workdir WM_DELETE_WINDOW {exit_cleanup}
  wm withdraw .

  if {[catch "image type Conflict"]} {
    workdir_images
  }
  # Ignore the dimensions, because they depend on the content.  Use
  # the remembered position though.
  if {[info exists cvscfg(workgeom)]} {
    regsub {\d+x\d+} $cvscfg(workgeom) {} winloc
    wm geometry .workdir $winloc
  }

  workdir_menus

  #
  # Top section - where we are, where the module is
  #
  frame .workdir.top -relief groove -border 2
  pack .workdir.top -side top -fill x

  label .workdir.top.lcwd -text "Current Directory"
  entry .workdir.top.tcwd -textvariable cwd
  bind .workdir.top.tcwd <Return> {change_dir "$cwd"}

  label .workdir.top.lmodule -text "Module Location"
  label .workdir.top.tmodule -textvariable module_dir -anchor w -relief groove

  label .workdir.top.ltagname -text "Directory Tag"
  label .workdir.top.ttagname -textvariable current_tagname -anchor w -relief groove

  # Make the Module Browser button prominent
  button .workdir.top.bmodbrowse -image Modules \
     -command { modbrowse_run $cvscfg(cvsroot) }

  label .workdir.top.lcvsroot -text "CVS Root"
  label .workdir.top.tcvsroot -textvariable cvscfg(cvsroot) -anchor w -relief groove

  grid columnconf .workdir.top 1 -weight 1
  grid rowconf .workdir.top 3 -weight 1
  grid .workdir.top.lcwd -column 0 -row 0 -sticky w
  grid .workdir.top.tcwd -column 1 -row 0 -columnspan 2 -padx 4 -sticky ew
  grid .workdir.top.lmodule -column 0 -row 1 -sticky nw
  grid .workdir.top.tmodule -column 1 -row 1 -columnspan 2 -padx 4 -pady 1 -sticky ew
  grid .workdir.top.bmodbrowse -column 2 -row 2 -rowspan 2 -sticky w
  grid .workdir.top.ltagname -column 0 -row 2 -sticky nw
  grid .workdir.top.ttagname -column 1 -row 2 -padx 4 -pady 1 -sticky new
  grid .workdir.top.lcvsroot -column 0 -row 3 -sticky nw
  grid .workdir.top.tcvsroot -column 1 -row 3 -padx 4 -pady 1 -sticky new


  # Pack the bottom before the middle so it doesnt disappear if
  # the window is resized smaller
  frame .workdir.bottom -relief groove -border 2 -height 128
  frame .workdir.bottom.filters
  pack .workdir.bottom -side bottom -fill x
  pack .workdir.bottom.filters -side top -fill x

  label .workdir.bottom.filters.showlbl -text "Show:" -anchor w
  entry .workdir.bottom.filters.showentry -textvariable cvscfg(file_filter) -width 12
  label .workdir.bottom.filters.hidelbl -text "   Hide:" -anchor w
  entry .workdir.bottom.filters.hideentry -width 12 \
     -textvariable cvsglb(default_ignore_filter)
  label .workdir.bottom.filters.space -text "    "
  button .workdir.bottom.filters.cleanbutton -text "Clean:" \
     -pady 0 -highlightthickness 0 \
     -command workdir_cleanup
  entry .workdir.bottom.filters.cleanentry -width 12 \
     -textvariable cvscfg(clean_these)
  bind .workdir.bottom.filters.showentry <Return> {setup_dir}
  bind .workdir.bottom.filters.hideentry <Return> {
     set cvsglb(default_ignore_filter) [.workdir.bottom.filters.hideentry get]
     setup_dir}
  bind .workdir.bottom.filters.cleanentry <Return> {workdir_cleanup}
  pack .workdir.bottom.filters.showlbl -side left
  pack .workdir.bottom.filters.showentry -side left
  pack .workdir.bottom.filters.hidelbl -side left
  pack .workdir.bottom.filters.hideentry -side left
  pack .workdir.bottom.filters.space -side left
  pack .workdir.bottom.filters.cleanbutton -side left -ipadx 2 -ipady 0
  pack .workdir.bottom.filters.cleanentry -side left

  frame .workdir.bottom.buttons
  frame .workdir.bottom.buttons.dirfuncs -relief groove -bd 2
  frame .workdir.bottom.buttons.cvsfuncs -relief groove -bd 2
  frame .workdir.bottom.buttons.modfuncs -relief groove -bd 2
  frame .workdir.bottom.buttons.close -relief groove -bd 2
  pack .workdir.bottom.buttons -side top -fill x -expand yes
  pack .workdir.bottom.buttons.dirfuncs -side left -fill x -expand yes
  pack .workdir.bottom.buttons.cvsfuncs -side left -fill x -expand yes
  pack .workdir.bottom.buttons.modfuncs -side left -fill x -expand yes
  pack .workdir.bottom.buttons.close -side left -fill both -expand yes

  #
  # Action buttons along the bottom of the screen.
  #
  button .workdir.bedit_files -image Fileedit \
     -command { workdir_edit_file [workdir_list_files] }
  button .workdir.bdelete_file -image Delete \
     -command { workdir_delete_file [workdir_list_files] }
  button .workdir.brefresh -image Refresh \
     -command setup_dir
  button .workdir.bconflict -image Conflict \
     -command { cvs_merge_conflict [workdir_list_files] }

  button .workdir.blogfile -image Branches \
     -command { cvs_logcanvas [workdir_list_files] }
  button .workdir.btag -image Tag \
     -command file_tag_dialog
  button .workdir.badd_files -image Add \
     -command { add_dialog [workdir_list_files] }
  button .workdir.bremove -image Remove \
     -command { subtract_dialog [workdir_list_files] }
  button .workdir.bdiff -image Diff \
     -command { cvs_diff [workdir_list_files] }
  button .workdir.bcheckin -image Checkin \
     -command commit_run
  button .workdir.bupdate -image Checkout \
     -command { cvs_update "BASE" "Normal" \
        "Remove" "No" " " [workdir_list_files] }
  button .workdir.beditflag_files -image Edit \
     -command { cvs_edit [workdir_list_files] }
  button .workdir.buneditflag_files -image Unedit \
     -command { cvs_unedit [workdir_list_files] }
  button .workdir.bclose -text "Close" \
     -padx 0 -pady 0 \
     -command exit_cleanup

  pack .workdir.bedit_files \
       .workdir.bdelete_file \
       .workdir.brefresh \
    -in .workdir.bottom.buttons.dirfuncs \
    -side left -ipadx 1 -ipady 1 -fill x -expand 1

  pack .workdir.blogfile \
       .workdir.bdiff \
       .workdir.badd_files \
       .workdir.bremove \
       .workdir.bconflict \
     -in .workdir.bottom.buttons.cvsfuncs \
     -side left -ipadx 1 -ipady 1 -fill x -expand 1

  pack .workdir.bcheckin \
       .workdir.bupdate \
       .workdir.btag \
     -in .workdir.bottom.buttons.modfuncs \
     -side left -ipadx 1 -ipady 1 -fill x -expand 1
  if {$cvscfg(econtrol)} {
    pack .workdir.beditflag_files \
         .workdir.buneditflag_files \
       -in .workdir.bottom.buttons.modfuncs \
       -side left -ipadx 1 -ipady 1 -fill x -expand 1
    }
  pack .workdir.bclose \
     -in .workdir.bottom.buttons.close \
     -side right -ipadx 0 -ipady 0 -fill both -expand 1

  set_tooltips .workdir.bedit_files \
     {"Edit the selected files"}
  set_tooltips .workdir.bdelete_file \
     {"Delete the selected files from the current directory"}
  set_tooltips .workdir.brefresh \
     {"Re-read the current directory"}
  set_tooltips .workdir.bconflict \
     {"Merge Conflicts using TkDiff"}
  set_tooltips .workdir.blogfile \
     {"See the revision log and branches of the selected files"}
  set_tooltips .workdir.badd_files \
     {"Add the selected files to the repository"}
  set_tooltips .workdir.btag \
     {"Tag the selected files"}
  set_tooltips .workdir.bremove \
     {"Remove the selected files from the repository"}
  set_tooltips .workdir.bcheckin \
     {"Check in (commit) the selected files to the repository"}
  set_tooltips .workdir.bupdate \
     {"Update (checkout, patch) the selected files from the repository"}
  if {$cvscfg(econtrol)} {
    set_tooltips .workdir.beditflag_files \
       {"Set the Edit flag on the selected files"}
    set_tooltips .workdir.buneditflag_files \
       {"Reset the Edit flag on the selected files"}
  }
  set_tooltips .workdir.bdiff \
     {"Compare the selected files with the repository version"}
  set_tooltips .workdir.top.bmodbrowse \
     {"Open the Module Browser"}
  set_tooltips .workdir.bclose \
     {"Close the Working Directory Browser"}


  #
  # Entry widget to be used for feedback
  #
  set feedback(cvs) [entry .workdir.bottom.feedback -width 55 -state disabled]
  pack .workdir.bottom.feedback -side bottom -fill x -expand yes

  frame .workdir.main
  pack .workdir.main -side bottom -fill both -expand 1 -fill both

  if {! [winfo ismapped .workdir]} {
    wm deiconify .workdir
  }

  # Do the directory listing.  We go to change_dir so that the original
  # working directory gets in the Go menu
  change_dir "[pwd]"
  gen_log:log T "LEAVE"
}

proc workdir_images {} {
  global cvscfg

  image create photo arr_up \
    -format gif -file [file join $cvscfg(bitmapdir) arrow_up.gif]
  image create photo arr_dn \
    -format gif -file [file join $cvscfg(bitmapdir) arrow_dn.gif]
  image create photo arh_up \
    -format gif -file [file join $cvscfg(bitmapdir) arrow_hl_up.gif]
  image create photo arh_dn \
    -format gif -file [file join $cvscfg(bitmapdir) arrow_hl_dn.gif]
  image create photo Fileview \
    -format gif -file [file join $cvscfg(bitmapdir) fileview.gif]
  image create photo Fileedit \
    -format gif -file [file join $cvscfg(bitmapdir) fileedit.gif]
  image create photo Delete \
    -format gif -file [file join $cvscfg(bitmapdir) delete.gif]
  image create photo Refresh \
    -format gif -file [file join $cvscfg(bitmapdir) loop-glasses.gif]
  image create photo Branches \
    -format gif -file [file join $cvscfg(bitmapdir) branch.gif]
  image create photo Add \
    -format gif -file [file join $cvscfg(bitmapdir) add.gif]
  image create photo Remove \
    -format gif -file [file join $cvscfg(bitmapdir) remove.gif]
  image create photo Diff \
    -format gif -file [file join $cvscfg(bitmapdir) diff.gif]
  image create photo Checkin \
    -format gif -file [file join $cvscfg(bitmapdir) checkin.gif]
  image create photo Checkout \
    -format gif -file [file join $cvscfg(bitmapdir) checkout.gif]
  image create photo Edit \
    -format gif -file [file join $cvscfg(bitmapdir) edit.gif]
  image create photo Unedit \
    -format gif -file [file join $cvscfg(bitmapdir) unedit.gif]
  image create photo Modules \
    -format gif -file [file join $cvscfg(bitmapdir) modbrowse.gif]

  image create photo Export \
    -format gif -file [file join $cvscfg(bitmapdir) export.gif]
  image create photo Tag \
    -format gif -file [file join $cvscfg(bitmapdir) tag.gif]
  image create photo Tags \
    -format gif -file [file join $cvscfg(bitmapdir) tags.gif]
  image create photo Mergebranch \
    -format gif -file [file join $cvscfg(bitmapdir) mergebranch.gif]
  image create photo Mergediff \
    -format gif -file [file join $cvscfg(bitmapdir) mergediff.gif]
  image create photo Conflict \
    -format gif -file [file join $cvscfg(bitmapdir) conflict.gif]

}

proc workdir_menus {} {
  global cvscfg
  global cvsmenu
  global usermenu
  global execmenu

  gen_log:log T "ENTER"
  set startdir "[pwd]"

  .workdir configure -menu .workdir.menubar
  menu .workdir.menubar

  #
  # Create the Menu bar
  #
  .workdir.menubar add cascade -label "File" -menu .workdir.menubar.file -underline 0
  menu .workdir.menubar.file
  .workdir.menubar add cascade -label "Reports" -menu .workdir.menubar.reports -underline 0
  menu .workdir.menubar.reports
  .workdir.menubar add cascade -label "Options" -menu .workdir.menubar.options -underline 0
  menu .workdir.menubar.options

  if { [info exists cvsmenu] || \
       [info exists usermenu] || \
       [info exists execmenu]} {
    .workdir.menubar add cascade -label "User Defined" -menu .workdir.menubar.user -underline 0
    menu .workdir.menubar.user
    gen_log:log T "Adding user defined menu"
  }
  .workdir.menubar add cascade -label "Go" -menu .workdir.menubar.goto -underline 0
  menu .workdir.menubar.goto
  .workdir.menubar add cascade -label "Help" -menu .workdir.menubar.help -underline 0
  menu .workdir.menubar.help

  #
  # Create the Menus
  #
  .workdir.menubar.file add command -label "Open" -underline 0 \
     -command { workdir_edit_file [workdir_list_files] }
  .workdir.menubar.file add command -label "Print" -underline 0 \
     -command { workdir_print_file  [workdir_list_files ] }
  .workdir.menubar.file add separator
  .workdir.menubar.file add command -label "Browse Modules" -underline 0 \
     -command { modbrowse_run $cvscfg(cvsroot) }
  .workdir.menubar.file add command -label "Cleanup" -underline 4 \
     -command workdir_cleanup
  .workdir.menubar.file add separator
  .workdir.menubar.file add command -label "Update" -underline 0 \
     -command { \
        cvs_update {BASE} {Normal} {Remove} {No} { } [workdir_list_files] }
  .workdir.menubar.file add command -label "Update with Options" -underline 7 \
     -command update_run
  .workdir.menubar.file add command -label "Commit" -underline 0 \
     -command commit_run
  .workdir.menubar.file add command -label "Add Files" -underline 0 \
     -command { add_dialog [workdir_list_files] }
  .workdir.menubar.file add command -label "Add Recursively" \
     -command { addir_dialog [workdir_list_files] }
  .workdir.menubar.file add command -label "Remove Files" -underline 0 \
     -command { subtract_dialog [workdir_list_files] }
  .workdir.menubar.file add command -label "Remove Recursively" \
     -command { subtractdir_dialog [workdir_list_files] }
  .workdir.menubar.file add command -label "Set Edit Flag (Edit)" -underline 15 \
     -command { cvs_edit [workdir_list_files] }
  .workdir.menubar.file add command -label "Reset Edit Flag (Unedit)" -underline 11 \
     -command { cvs_unedit [workdir_list_files] }
  .workdir.menubar.file add command -label "Tag Files" -underline 0 \
     -command file_tag_dialog
  .workdir.menubar.file add command -label "Log Browse" -underline 0 \
     -command { cvs_logcanvas [workdir_list_files] }
  .workdir.menubar.file add command -label "Merge Conflict" -underline 0 \
     -command { cvs_merge_conflict [workdir_list_files] }
  .workdir.menubar.file add separator
  .workdir.menubar.file add command -label "Shell window" -underline 0 \
     -command {eval exec $cvscfg(shell) >& $cvscfg(null) &}
  .workdir.menubar.file add separator
  .workdir.menubar.file add command -label Exit -underline 1 \
     -command { module_exit; exit_cleanup }

  .workdir.menubar.reports add command -label "Check Directory" -underline 0 \
     -command { cvs_check [workdir_list_files] }
  .workdir.menubar.reports add command -label "CVS status" -underline 4 \
     -command { cvs_status [workdir_list_files] }
  .workdir.menubar.reports add command -label "Sticky status" -underline 4 \
     -command { cvs_tag_status [workdir_list_files] }
  .workdir.menubar.reports add command -label "CVS diff" -underline 4 \
     -command { cvs_diff [workdir_list_files] }
  .workdir.menubar.reports add command -label "CVS log" -underline 4 \
     -command { cvs_log [workdir_list_files] }
  .workdir.menubar.reports add command -label "CVS annotate" -underline 4 \
     -command { cvs_annotate [workdir_list_files] }

  set selcolor [option get .workdir selectColor selectColor]
  .workdir.menubar.options add checkbutton -label "Show hidden files" \
     -variable cvscfg(allfiles) -onvalue true -offvalue false \
     -selectcolor $selcolor -command setup_dir
  .workdir.menubar.options add checkbutton -label "Automatic directory status" \
     -variable cvscfg(auto_status) -onvalue true -offvalue false \
     -selectcolor $selcolor
  .workdir.menubar.options add checkbutton -label "Confirmation Dialogs" \
     -variable cvscfg(confirm_prompt) -onvalue true -offvalue false \
     -selectcolor $selcolor
  .workdir.menubar.options add separator
  .workdir.menubar.options add checkbutton -label "Editors Column" \
     -variable cvscfg(econtrol) -onvalue true -offvalue false \
     -selectcolor $selcolor -command econtrol_toggle
  .workdir.menubar.options add checkbutton -label "Status Column" \
     -variable cvscfg(showstatcol) -onvalue true -offvalue false \
     -selectcolor $selcolor -command "DirCanvas:build .workdir.main"
  .workdir.menubar.options add checkbutton -label "Date Column" \
     -variable cvscfg(showdatecol) -onvalue true -offvalue false \
     -selectcolor $selcolor -command "DirCanvas:build .workdir.main"
  .workdir.menubar.options add separator
  .workdir.menubar.options add checkbutton -label "Report Check recursively" \
     -variable cvscfg(checkrecursive) -onvalue {} -offvalue -l \
     -selectcolor $selcolor
  .workdir.menubar.options add checkbutton -label "Report Status Recursively" \
     -variable cvscfg(recurse) -onvalue true -offvalue false \
     -selectcolor $selcolor
  .workdir.menubar.options add cascade -label "CVS Status Detail" \
     -menu .workdir.menubar.options.report_detail
  .workdir.menubar.options add cascade -label "CVS Log Detail" \
     -menu .workdir.menubar.options.logfile_detail
  .workdir.menubar.options add separator
  .workdir.menubar.options add checkbutton -label "Tracing On/Off" \
     -variable cvscfg(logging) -onvalue true -offvalue false \
     -selectcolor $selcolor -command log_toggle
  .workdir.menubar.options add cascade -label "Trace Level" \
     -menu .workdir.menubar.options.loglevel
  .workdir.menubar.options add separator
  .workdir.menubar.options add command -label "Save Options" -underline 0 \
     -command save_options

  menu .workdir.menubar.options.loglevel
  .workdir.menubar.options.loglevel add checkbutton -label "CVS commands (C)" \
     -variable logclass(C) -onvalue "C" -offvalue "" \
     -selectcolor $selcolor -command gen_log:changeclass
  .workdir.menubar.options.loglevel add checkbutton -label "File creation/deletion (F)"\
     -variable logclass(F) -onvalue F -offvalue "" \
     -selectcolor $selcolor -command gen_log:changeclass
  .workdir.menubar.options.loglevel add checkbutton -label "Function entry/exit (T)" \
     -variable logclass(T) -onvalue T -offvalue "" \
     -selectcolor $selcolor -command gen_log:changeclass
  .workdir.menubar.options.loglevel add checkbutton -label "Debugging (D)" \
     -variable logclass(D) -onvalue D -offvalue "" \
     -selectcolor $selcolor -command gen_log:changeclass

  menu .workdir.menubar.options.report_detail
  .workdir.menubar.options.report_detail add radiobutton -label "Verbose" \
     -variable cvscfg(rdetail) -value "verbose" -selectcolor $selcolor
  .workdir.menubar.options.report_detail add radiobutton -label "Summary" \
     -variable cvscfg(rdetail) -value "summary" -selectcolor $selcolor
  .workdir.menubar.options.report_detail add radiobutton -label "Terse" \
     -variable cvscfg(rdetail) -value "terse" -selectcolor $selcolor

  menu .workdir.menubar.options.logfile_detail
  .workdir.menubar.options.logfile_detail add radiobutton -label "Latest" \
     -variable cvscfg(ldetail) -value "latest" -selectcolor $selcolor
  .workdir.menubar.options.logfile_detail add radiobutton -label "Summary" \
     -variable cvscfg(ldetail) -value "summary" -selectcolor $selcolor
  .workdir.menubar.options.logfile_detail add radiobutton -label "Verbose" \
     -variable cvscfg(ldetail) -value "verbose" -selectcolor $selcolor

  .workdir.menubar.goto add command -label "Home" \
     -command {change_dir $cvscfg(home)}
  .workdir.menubar.goto add command -label $startdir \
     -command [format {change_dir "%s"} $startdir]
  .workdir.menubar.goto add command -label $cvscfg(lastdir) \
     -command [format {change_dir "%s"} $cvscfg(lastdir)]

  .workdir.menubar.help add command -label "About TkCVS" -underline 0 \
     -command aboutbox
  .workdir.menubar.help add command -label "About CVS" -underline 6 \
     -command cvs_version
  .workdir.menubar.help add command -label "About Wish" -underline 6 \
     -command wish_version
  .workdir.menubar.help add separator
  .workdir.menubar.help add command -label "Working with TkCVS" \
     -command working_with_tkcvs
  .workdir.menubar.help add command -label "Checking out Modules" \
     -command checking_out_modules
  .workdir.menubar.help add command -label "Exporting" \
     -command exporting
  .workdir.menubar.help add command -label "Tagging and Branching" \
     -command tagging_and_branching
  .workdir.menubar.help add command -label "Importing New Modules" \
     -command importing_new_modules
  .workdir.menubar.help add separator
  .workdir.menubar.help add command -label "Current Directory Display" \
     -command current_directory
  .workdir.menubar.help add command -label "Buttons" \
     -command buttons_help
  .workdir.menubar.help add separator
  .workdir.menubar.help add command -label "Module Browser" \
     -command module_browser
  .workdir.menubar.help add command -label "File Browser" \
     -command file_browser
  .workdir.menubar.help add command -label "Log Browser" \
     -command log_browser
  .workdir.menubar.help add separator
  .workdir.menubar.help add command -label "Configuration Files" \
     -command configuration_files
  .workdir.menubar.help add command -label "Environment Variables" \
     -command environment_variables
  .workdir.menubar.help add command -label "Command Line Options" \
     -command cli_options
  .workdir.menubar.help add command -label "User Defined Menu" \
     -command user_defined_menu
  .workdir.menubar.help add command -label "CVS modules File" \
     -command cvs_modules_file

  #
  # Add user commands to the menu.
  #
  if {[info exists cvsmenu]} {
    foreach item [array names cvsmenu] {
      .workdir.menubar.user add command -label $item \
         -command "eval cvs_usercmd $cvsmenu($item) \[workdir_list_files\]"
    }
  }
  if {[info exists usermenu]} {
    .workdir.menubar.user add separator
    foreach item [array names usermenu] {
      .workdir.menubar.user add command -label $item \
         -command "eval cvs_catchcmd $usermenu($item) \[workdir_list_files\]"
    }
  }
  if {[info exists execmenu]} {
    .workdir.menubar.user add separator
    foreach item [array names execmenu] {
      .workdir.menubar.user add command -label $item \
         -command "eval cvs_execcmd $execmenu($item) \[workdir_list_files\]"
    }
  }
  gen_log:log T "LEAVE"
}

proc workdir_list_files {} {
  global cvscfg
  global cvsglb

  gen_log:log T "ENTER (cvsglb(current_selection) = $cvsglb(current_selection))"

  for {set i 0} {$i < [llength $cvsglb(current_selection)]} {incr i} {
    set item [lindex $cvsglb(current_selection) $i]
    regsub {^no file } $item "" item
    # regsub here causes file isfile to return 0.  You have to do it in each
    # proc, just before the cvs command, after file tests have been done.
    #regsub -all {\$} $item {\$} item
    set cvsglb(current_selection) [lreplace $cvsglb(current_selection) $i $i $item]
  }
  gen_log:log T "LEAVE -- ($cvsglb(current_selection))"
  return $cvsglb(current_selection)
}

proc workdir_edit_command {file} {
  global cvscfg

  gen_log:log T "ENTER ($file)"
  if {[info exists cvscfg(editors)]} {
    foreach {editor pattern} $cvscfg(editors) {
      if {[string match $pattern $file]} {
        return "$editor \"$file\""
      }
    }
  }
  return "$cvscfg(editor) $cvscfg(editorargs) \"$file\""
}

proc workdir_edit_file {args} {
  global cvscfg
  global cwd
  global feedback

  gen_log:log T "ENTER ($args)"

  set filelist [join $args]
  if {$filelist == ""} {
    cvsfail "Please select some files to edit first!"
    return
  }

  feedback_cvs $feedback(cvs) "Building scroll list, please wait!"
  gen_log:log D "$filelist"
  foreach file $filelist {
    if {[file isdirectory $file]} {
      change_dir "$file"
    } else {
      feedback_cvs $feedback(cvs) "Starting editor, please wait!"
      if {[file isfile $file]} {
        regsub -all {\$} $file {\$} file
        set commandline "[workdir_edit_command $file] >& $cvscfg(null) &"
        gen_log:log C "$commandline"
        eval "exec $commandline"
      } else {
        gen_log:log D "$file is not a plain file"
      }
    }
  }
  feedback_cvs $feedback(cvs) ""
  gen_log:log T "LEAVE"
}

#------------------------------------------------------
# Update the "Go" menu for directories we can go to
# new_dir - the directory we're going to
# doPwd   - tells whether the directory path has
#           been specified  1 means relative to cwd
#                           0 means fully path specified
#-------------------------------------------------------
proc update_go {new_dir} {
  global cvscfg
  global dirlist
  global maxdirs
  global dirlen
  global cwd

  gen_log:log T "ENTER ($new_dir)"
  if {$new_dir == "."} { return }
  if {$new_dir == "$cvscfg(home)"} { return }
  if {$new_dir == ".."} {
    set new_dir [file dirname "$cwd"]
  }

  if {[llength $dirlist] < 1} {
    lappend dirlist $cvscfg(lastdir)
  }
  if {[file pathtype $new_dir] == "relative"} {
    # Get full pathname of directory
    set new_dir [format {%s/%s} "$cwd" $new_dir]
  }

  # Check if already in Go list
  gen_log:log D "looking for \"$new_dir\" in \"$dirlist\""
  set dirlocation  [lsearch -exact $dirlist $new_dir]

  # Move a directory already in the list to the top of the list
  if {$dirlocation != -1} {
    set dirlist [lreplace $dirlist $dirlocation $dirlocation ]
    set dirlist [linsert $dirlist 0 $new_dir]
  } else {
    set dirlist [linsert $dirlist 0 $new_dir]
  }
  gen_log:log D "New dirlist is \"$dirlist\""
  set dirlen  [llength $dirlist]

  # Truncate end of directory list if we have too many directories
  if {$dirlen > $maxdirs} {
    set dirlen [incr dirlen -1]
    set dirlist [lreplace $dirlist $dirlen $dirlen ]
  }

  # Destroy old menu selections for "Go"
  destroy .workdir.menubar.goto
  menu .workdir.menubar.goto
  .workdir.menubar.goto add command -label "Home" \
     -command {change_dir $cvscfg(home)}

  # Rebuild menu selections for "Go" with new dirlist
  for {set i 0} {$i < $dirlen} {incr i 1} {
    set tmpdir [lindex $dirlist $i]
    .workdir.menubar.goto add command -label $tmpdir \
       -command [format {change_dir "%s"} $tmpdir]
  }
  gen_log:log T "LEAVE"
}

proc change_dir {new_dir} {
  global cwd

  gen_log:log T "ENTER ($new_dir)"
  if {![file exists $new_dir]} {
    set cwd [pwd]
    cvsfail "Directory $new_dir doesn\'t exist!"
    return
  }
  update_go $new_dir
  set cwd $new_dir
  setup_dir

  gen_log:log T "LEAVE"
}

proc setup_dir { } {
  #
  # Call this when entering a directory.  It puts all of the file names
  # in the listbox, and reads the CVS or directory.
  #
  global cwd
  global module_dir
  global incvs
  global cvscfg
  global current_tagname
  global cvsglb

  gen_log:log T "ENTER"

  if { ! [winfo exists .workdir.main] } {
    workdir_setup
    return
  } else {
    DirCanvas:deltree .workdir.main
  }

  if {![file isdirectory $cwd]} {
    gen_log:log D "$cwd is not a directory"
    gen_log:log T "LEAVE -- $cwd is not a directory"
    return
  }

  cd $cwd
  gen_log:log F "CD [pwd]"
  cvsroot_check
  gen_log:log D " incvs = $incvs"

  DirCanvas:create .workdir.main \
    -relief flat -bd 0 -highlightthickness 0 \
    -width 100 -height 350

  set module_dir "Not in the repository"
  set current_tagname "No directory tag"

  set cvsglb(current_selection) {}

  set cvscfg(ignore_file_filter) $cvsglb(default_ignore_filter)

  if { [ file exists ".cvsignore" ] } {
    set fileId [ open ".cvsignore" "r" ]
    while { [ eof $fileId ] == 0 } {
      gets $fileId line
      append cvscfg(ignore_file_filter) " $line"
    }
    close $fileId
  }

  set filelist [ getFiles ]
  directory_list $filelist

  gen_log:log T "LEAVE"
}

proc directory_list { filenames } {
  global incvs
  global cvs
  global cwd
  global cvscfg
  global cvsglb
  global Filelist

  gen_log:log T "ENTER ($filenames)"

  if {[info exists Filelist]} {
    unset Filelist
  }

  busy_start .workdir.main

  gen_log:log F "processing files in the local directory"
  set cwd [pwd]
  # Select from those files only the ones we want (e.g., no CVS dirs)
  foreach i $filenames {
    if { $i == "." } {
      gen_log:log D "SKIPPING ."
      continue
    }
    if {[file isdirectory $i]} {
      if {[isCmDirectory $i]} {
        # Read the CVS files but don't list the directory
        if {$i == "CVS"} {
          read_cvs_dir [file join $cwd $i]
          continue
        }
      }
      if {[file exists [file join $i "CVS"]]} {
        set Filelist($i:status) "<directory:CVS>"
      } else {
        set Filelist($i:status) "<directory>"
      }
    } else {
      if {$incvs} {
        set Filelist($i:status) "?"
      } else {
        set Filelist($i:status) "<file>"
      }
    }
    set Filelist($i:wrev) ""
    set Filelist($i:stickytag) ""
    set Filelist($i:option) ""
    if {[string match "<direc*" $Filelist($i:status)]} {
      set Filelist($i:date) ""
    } else {
      # Prepending ./ to the filename prevents tilde expansion
      catch {set Filelist($i:date) \
         [clock format [file mtime ./$i] -format $cvscfg(dateformat)]}
    }
  }

  if {$incvs} {
    gen_log:log F "processing files that CVS knows about"
    # gets cvs status in current directory only, pulling out lines that include
    # Status: or Sticky Tag:, putting each file's info (name, status, and tag)
    # into an array.
    set commandline "$cvs -n -q -l status -l"
    gen_log:log C "$commandline"
    catch {eval "exec $commandline"} raw_log

    set log_lines [split $raw_log "\n"]
    foreach logline $log_lines {
      if {[string match "File:*" $logline]} {
        regsub -all {\t+} $logline "\t" logline
        set line [split [string trim $logline] "\t"]
        gen_log:log D "$line"
        # Should be able to do these regsubs in one expression
        regsub {File: } [lindex $line 0] "" filename
        regsub {\s*$} $filename "" filename
        if {[string match "no file *" $filename]} {
          regsub {^no file } $filename "" filename
        }
        regsub {Status: } [lindex $line 1] "" status
        set Filelist($filename:status) $status
        # Don't set editors to null because we'll use its presence
        # or absence to see if we need to re-read the repository when
        # we ask to map the editors column
        #set Filelist($filename:editors) ""
      } elseif {[string match "*Working revision:*" $logline]} {
        regsub -all {\t+} $logline "\t" logline
        set line [split [string trim $logline] "\t"]
        gen_log:log D "$line"
        set revision [lindex $line 1]
        regsub {New .*} $revision "New" revision
        set date [lindex $line 2]
        gen_log:log D "--- $date ---"
        # The date field is not supplied to remote clients.
        if {$date == "" || [string match "New *" $date ] || \
            [string match "Result *" $date]} {
          ; # Leave as is
        } else {
          set juliandate [clock scan $date -gmt yes]
          set date [clock format $juliandate -format $cvscfg(dateformat)]
          set Filelist($filename:date) $date
        }
        set Filelist($filename:wrev) $revision
        set Filelist($filename:status) $status
      } elseif {[string match "*Sticky Tag:*" $logline]} {
        regsub -all {\t+} $logline "\t" logline
        set line [split [string trim $logline] "\t"]
        gen_log:log D "$line"
        set tagline [lindex $line 1]
        set t0 [lindex $tagline 0]
        set t1 [lrange $tagline 1 end]
        set stickytag ""
        if { $t0 == "(none)" } {
          set stickytag " on trunk"
        } elseif {[string match "(branch:*" $t1 ]} {
          regsub {\(branch: (.*)\)} $t1 {\1} t1
          set stickytag " on $t0  branch"
        } elseif {[string match "(revision:*" $t1 ]} {
          set stickytag " $t0"
        }
        set Filelist($filename:stickytag) "$revision $stickytag"
      } elseif {[string match "*Sticky Options:*" $logline]} {
        regsub -all {\t+} $logline "\t" logline
        set line [split [string trim $logline] "\t"]
        gen_log:log D "$line"
        set option [lindex $line 1]
        set Filelist($filename:option) $option
      }
    }
  }

  if {$incvs && $cvscfg(econtrol)} {
    set command "$cvs -n -q -l editors -l"
    gen_log:log C "$command"
    catch {eval "exec $command"} raw_log2

    set log_lines [split $raw_log2 "\n"]
    foreach logline $log_lines {
      set line [split [string trim $logline] "\t"]
      gen_log:log D "$line"
      set ell [llength $line]
      # ? files will show up in cvs editors output under certain conditions
      if {$ell < 2} {
        continue
      }
      #if there is no filename, then the first field is another editor
      if {$ell < 5} {
        #gen_log:log D "[llength $line]"
        #gen_log:log D "[string index $line 0]"
        append editors ",[lindex $line 0]"
      } else {
        set filename [lindex $line 0]
        set editors [lindex $line 1]
      }
      gen_log:log D " $filename   $editors"
        set Filelist($filename:editors) $editors
    }
  }

  gen_log:log D "Sending all files to the canvas"
  foreach i [array names Filelist *:status] {
    regsub {:status$} $i "" j
    # If it's locally removed or missing, it may not have
    # gotten a date especially on a remote client.
    if {! [info exists Filelist($j:date)]} {
      set Filelist($j:date) ""
    }
    DirCanvas:newitem .workdir.main "$j"
  }

  busy_done .workdir.main

  gen_log:log T "LEAVE"
}

proc read_cvs_dir {dirname} {
#
# Reads a CVS directory
#
  global module_dir
  global cvscfg
  global current_tagname

  gen_log:log T "ENTER ($dirname)"
  if {[file isdirectory $dirname]} {
    if {[file isfile [file join $dirname Repository]]} {
      set f [open [file join $dirname Repository] r]
      gets $f module_dir
      close $f
      gen_log:log D "  MODULE $module_dir"
      if {[file isfile [file join $dirname Root]]} {
        set f [open [file join $dirname Root] r]
        gets $f cvscfg(cvsroot)
        close $f
        gen_log:log D "  CVSROOT $cvscfg(cvsroot)"
      }
      if {[file isfile [file join $dirname Tag]]} {
        set f [open [file join $dirname Tag] r]
        gets $f current_tagname
        close $f
        # T = branch tag, N = non-branch, D = sticky date
        #set current_tagname [string trimleft $current_tagname "TND"]
        set current_tagname [string range $current_tagname 1 end]
        gen_log:log D "  BRANCH TAG $current_tagname"
      }
    } else {
      cvsfail "Repository file not found in $dirname"
    }
  } else {
    cvsfail "$dirname is not a directory"
  }
  gen_log:log T "LEAVE"
}

proc workdir_cleanup {} {
  global cvscfg

  gen_log:log T "ENTER"
  set list [ split $cvscfg(clean_these) " " ]
  if { [ are_you_sure "You are about to delete:\n" $list] == 1 } {
    foreach item $list {
      gen_log:log D "item $item"
      if { $item != "" } {
        set rmitem [lsort [glob -nocomplain $item]]
        if {$rmitem != ""} {
          gen_log:log F "DELETE $rmitem"
          eval file delete -force -- $rmitem
        }
      } else {
        nop
      }
    }
    setup_dir
  }
  gen_log:log T "LEAVE"
}

proc workdir_delete_file {args} {
  global cvscfg

  gen_log:log T "ENTER ($args)"

  set filelist [join $args]
  if {$filelist == ""} {
    cvsfail "Please select some files to delete first!"
    return
  }

  if { [ are_you_sure "This will delete these files from your local, working directory:\n" $filelist ] == 1 } {
    gen_log:log F "DELETE $filelist"
    eval file delete -force -- $filelist
    setup_dir
  }
  gen_log:log T "LEAVE"
}

proc econtrol_toggle { } {
#
# Turn display of "Editors" column on and off
#
  global Filelist
  global cvscfg

  gen_log:log T "ENTER"

  if {$cvscfg(econtrol)} {
    # Add or remove the bottom editing buttons
    if {! [winfo viewable .workdir.beditflag_files]} {
      pack .workdir.beditflag_files .workdir.buneditflag_files \
        -in .workdir.bottom.buttons.modfuncs \
               -side left -ipadx 1 -ipady 1 -fill x -expand 1
    }
  } else {
    if {[winfo viewable .workdir.beditflag_files]} {
      pack forget .workdir.beditflag_files .workdir.buneditflag_files
    }
  }

  gen_log:log D "[array names Filelist *:editors]"
  if { [llength [array names Filelist *:editors]] > 0 } {
    DirCanvas:build .workdir.main
  } else {
    setup_dir
  }
}

proc are_you_sure {mess args} {
#
# General posting message
#
  global cvscfg

  gen_log:log T "ENTER ($mess $args)"

  set filelist [join $args]
  if {$cvscfg(confirm_prompt)} {
    append mess "\n"
    set indent "      "

    foreach item $filelist {
      if { $item != {} } {
        append mess " $indent"
        append mess " $item\n"
      }
    }
    append mess "\nAre you sure?"
    if {[cvsconfirm $mess] == 1} {
      gen_log:log T "LEAVE 0"
      return 0
    }
  }
  gen_log:log T "LEAVE 1"
  return 1
}

proc busy_start {w} {

  foreach widget [winfo children $w] {
    catch {$widget config -cursor watch}
  }
  update idletasks
}

proc busy_done {w} {

  foreach widget [winfo children $w] {
    catch {$widget config -cursor ""}
  }
}

proc workdir_print_file {args} {
  global cvscfg

  gen_log:log T "ENTER ($args)"

  set filelist [join $args]
  if {$filelist == ""} {
    cvsfail "Please select some files to print first!"
    return
  }

  set mess "This will print these files:\n\n"
  foreach file $filelist {
    append mess "   $file\n"
  }
  append mess "\nUsing $cvscfg(print_cmd)\n"
  append mess "\nAre you sure?"
  if {[cvsconfirm $mess] == 0} {
    set final_result ""
    foreach file $filelist {
      gen_log:log F "$cvscfg(print_cmd) \"$file\""
      catch { eval exec $cvscfg(print_cmd) \"$file\" } file_result
      if { $file_result != "" } {
        set final_result "$final_result\n$file_result"
      }
    }
    if { $final_result != "" } {
      view_output "Print" $final_result
    }
  }
  gen_log:log T "LEAVE"
}

proc cvsroot_check {} {
  global cvsglb
  global cvscfg
  global incvs
  global env

  gen_log:log T "ENTER"
  set incvs 0
  if {[file isfile [file join . CVS Root]]} {
    set f [open [file join . CVS Root] r]
    gets $f root
    close $f
    set incvs 1
    set cvscfg(cvsroot) $root
  }
  gen_log:log T " cvsroot: $cvscfg(cvsroot)"
  if {[string match "*:*" $cvscfg(cvsroot)]} {
    set cvsglb(remote) 1
  } else {
    set cvsglb(remote) 0
  }
  gen_log:log T " remote=$cvsglb(remote)"
  gen_log:log T "LEAVE"
}

proc nop {} {}

proc disabled {} {
  cvsok "Command disabled."
}

proc isCmDirectory { file } {
  switch -- $file  {
    "CVS"  -
    "RCS"  -
    "SCCS" { set value 1 }
    default { set value 0 }
  }
  return $value
}

# Get the files in the current working directory.  Use the file_filter
# values Add hidden files if desired by the user.  Sort them to match
# the ordering that will be returned by cvs commands (this matches the
# default ls ordering.).
proc getFiles { } {
  global cvscfg
  global cvsglb

  gen_log:log T "ENTER"
  set filelist ""

  # make sure the file filter is at least set to "*".
  if { $cvscfg(file_filter) == "" } {
    set cvscfg(file_filter) "*"
  }

  # get the initial file list, including hidden if requested
  if {$cvscfg(allfiles)} {
    # get hidden as well
    foreach item $cvscfg(file_filter) {
      set filelist [ concat [ glob -nocomplain .$item $item ] $filelist ]
    }
  } else {
    foreach item $cvscfg(file_filter) {
      set filelist [ concat [ glob -nocomplain $item ] $filelist ]
    }
  }
  #gen_log:log D "filelist ($filelist)"

  # ignore files if requested
  if { $cvscfg(ignore_file_filter) != "" } {
    foreach item $cvscfg(ignore_file_filter) {
      # for each pattern
      if { $item != "*" } {
        # if not "*"
        while { [set idx [lsearch $filelist $item]] != -1 } {
          # for each occurence, delete
          catch { set filelist [ lreplace $filelist $idx $idx ] }
        }
      }
    }
  }

  # make sure "." is always in the list for 'cd' purposes
  if { ( [ lsearch -exact $filelist "." ] == -1 ) } {
    set filelist [ concat "." $filelist ]
  }

  # make sure ".." is always in the list for 'cd' purposes
  if { ( [ lsearch -exact $filelist ".." ] == -1 ) } {
    set filelist [ concat ".." $filelist ]
  }

  # sort it
  set filelist [ lsort $filelist ]

  # if this directory is under CVS and CVS is not in the list, add it. Its
  # presence is needed for later processing
  if { ( [ file exists "CVS" ] ) &&
       ( [ lsearch -exact $filelist "CVS" ] == -1 ) } {
    #puts "********* added CVS"
    catch { set filelist [ concat "CVS" $filelist ] }
  }

  set cvscfg(ignore_file_filter) $cvsglb(default_ignore_filter)
  gen_log:log T "return ($filelist)"
  return $filelist
}

proc feedback_cvs { e message } {
#
# This code is adapted from the text "Practical Programming in
# Tcl and Tk", by Brent B. Welch (see page 209)
# An entry widget is used because it won't change size
# base on the message length, and it can be scrolled by
# dragging with button 2.
# Author: Eugene Lee, Aerospace Corporation, 9/6/95
#
  global feedback
  global cvscfg

  $e config -state normal
  $e delete 0 end
  $e insert 0 $message
  # Leave the entry in a read-only state
  $e config -state disabled

  # Force a disable update
  update idletasks
}

proc log_toggle { } {
  global cvscfg

  if {$cvscfg(logging)} {
    gen_log:init
  } else {
    gen_log:quit
  }
}

proc exit_cleanup { } {
  global cvscfg

  if {[winfo exists .workdir]} {
    set cvscfg(workgeom) [wm geometry .workdir]
  }
  if {[winfo exists .trace]} {
    set cvscfg(tracgeom) [wm geometry .trace]
  }

  # If the module browser exists, don't quit tkcvs, just the workdir
  if {[winfo exists .modbrowse]} {
    destroy .workdir
    return
  }

  save_options
  set pid [pid]
  gen_log:log F "DELETE $cvscfg(tmpdir)/cvstmpdir.$pid"
  catch [file delete -force [file join $cvscfg(tmpdir) cvstmpdir.$pid]]
  destroy .workdir
  exit
}

proc save_options { } {
#
# Save the options which are configurable from the GUI
#
  global cvscfg

  gen_log:log T "ENTER"

  # There are two kinds of options we can set
  set BOOLopts { allfiles auto_status confirm_prompt econtrol \
                 showstatcol showdatecol checkrecursive recurse logging }
  set STRGopts { file_filter ignore_file_filter clean_these \
                 printer rdetail ldetail log_classes lastdir \
                 workgeom modgeom tracgeom}

  # set this to current directory, so we'll add it to the menu next time
  if ([catch pwd]) {
    return
  }
  set cvscfg(lastdir) [pwd]

  # Save the list so we can keep track of what we've done
  set BOOLset $BOOLopts
  set STRGset $STRGopts

  set optfile [file join $cvscfg(home) .tkcvs]
  set bakfile [file join $cvscfg(home) .tkcvs.bak]
  # Save the old .tkcvs file
  gen_log:log F "MOVE $optfile $bakfile"
  catch [file rename -force $optfile $bakfile]

  gen_log:log F "OPEN $optfile"
  if {[catch {set fo [open $optfile w]}]} {
    cvsfail "Cannot open $optfile for writing"
    return
  }
  gen_log:log F "OPEN $bakfile"

  if {! [catch {set fi [open $bakfile r]}]} {
    while { [eof $fi] == 0 } {
      gets $fi line
      set match 0
      if {[regexp {^#} $line]} {
        # Don't try to scan comments.
        #gen_log:log D "PASSING \"$line\""
        puts $fo "$line"
        continue
      } else {
        foreach opt $BOOLopts {
          if {! [info exists cvscfg($opt)]} { continue }
          if {[string match "*set *cvscfg($opt)*" $line]} {
            # Print it and remove it from the list
            gen_log:log D "REPLACING $line  w/  set cvscfg($opt) $cvscfg($opt)"
            puts $fo "set cvscfg($opt) $cvscfg($opt)"
            set idx [lsearch $BOOLset $opt]
            set BOOLset [lreplace $BOOLset $idx $idx]
            set match 1
            break
          }
        }
        foreach opt $STRGopts {
          if {! [info exists cvscfg($opt)]} { continue }
          if {[string match "*set *cvscfg($opt)*" $line]} {
            # Print it and remove it from the list
            gen_log:log D "REPLACING $line"
            puts $fo "set cvscfg($opt) \"$cvscfg($opt)\""
            set idx [lsearch $STRGset $opt]
            set STRGset [lreplace $STRGset $idx $idx]
            set match 1
            break
          }
        }
        if {$match == 0} {
          # We didn't do a replacement
          gen_log:log D "PASSING \"$line\""
          # If we don't check this, we get an extra blank line every time
          # we save the file.  Messy.
          if {[eof $fi] == 1} { break }
          puts $fo "$line"
        }
      }
    }
    close $fi
  }

  # Print what's left over
  foreach opt $BOOLset {
    if {! [info exists cvscfg($opt)]} { continue }
    gen_log:log D "ADDING cvscfg($opt) $cvscfg($opt)"
    puts $fo "set cvscfg($opt) $cvscfg($opt)"
  }

  foreach opt $STRGset {
    if {! [info exists cvscfg($opt)]} { continue }
    gen_log:log D "ADDING cvscfg($opt) \"$cvscfg($opt)\""
    puts $fo "set cvscfg($opt) \"$cvscfg($opt)\""
  }

  close $fo
  gen_log:log T "LEAVE"
}

