#
# TCL Library for tkCVS
#

#
# $Id: logcanvas.tcl,v 1.13 1995/11/13 05:16:55 davide Exp $
# 
# Contains procedures used for the log canvas for tkCVS.
#

#
# Globals used in drawing canvases
#

# Height and width to draw boxes
set cvscanv(boxx) 60
set cvscanv(boxy) 30
# Gaps between boxes
set cvscanv(gapx) [expr $cvscanv(boxx) + 10]
set cvscanv(gapy) [expr $cvscanv(boxy) + 10]
# Indent at top left of canvas
set cvscanv(indx) 20
set cvscanv(indy) 20
# Static type variables used while drawing on the canvas.
set cvscanv(nextx) $cvscanv(indx)
set cvscanv(nexty) $cvscanv(indy)
set cvscanv(indents) 0
set cvscanv(xhigh) [expr $cvscanv(boxx) + $cvscanv(indx)]
set cvscanv(yhigh) [expr $cvscanv(boxy) + $cvscanv(indy)]
set cvscanv(xlow) 0
set cvscanv(ylow) 0

proc new_logcanvas {localfile filelog} {
#
# Creates a new log canvas.  filelog must be the output of a cvs
# log or rlog command.  If localfile is not "no file" then it is
# the file name in the local directory that this applies to.
#
  global cvs
  global cvscanv
  global tagvalues

  static {canvasnum 0}

  # Make the canvas

  incr canvasnum
  set logcanvas ".logcanvas$canvasnum"
  toplevel $logcanvas

  frame $logcanvas.up -relief groove -border 2
  frame $logcanvas.up.left
  frame $logcanvas.up.right
  frame $logcanvas.up1 -relief groove -border 2
  frame $logcanvas.up1.left
  frame $logcanvas.up1.right
  frame $logcanvas.up2 -relief groove -border 2
  frame $logcanvas.up2.left
  frame $logcanvas.up2.right
  frame $logcanvas.down -relief groove -border 2

  pack $logcanvas.up -side top -fill x
  pack $logcanvas.up.left -side left -fill both
  pack $logcanvas.up.right -side left -fill both -expand 1
  pack $logcanvas.up1 -side top -fill x
  pack $logcanvas.up1.left -side left -fill both
  pack $logcanvas.up1.right -side left -fill both -expand 1
  pack $logcanvas.up2 -side top -fill x
  pack $logcanvas.up2.left -side left -fill both
  pack $logcanvas.up2.right -side left -fill both -expand 1
  pack $logcanvas.down -side bottom -fill x

  label $logcanvas.lfname -anchor w -text "RCS File Name"
  entry $logcanvas.tfname
  # This is a hidden entry that stores the local file name.
  entry $logcanvas.tlocalfile
  $logcanvas.tlocalfile delete 0 end
  $logcanvas.tlocalfile insert end $localfile

  label $logcanvas.lvers1 -anchor w -text "Version A"
  label $logcanvas.lwho1 -anchor w -text "Created by"
  label $logcanvas.ldate1 -anchor w -text "Date"
  label $logcanvas.lcomment1 -anchor w -text "Comment"

  entry $logcanvas.tvers1 -relief sunken
  label $logcanvas.twho1 -anchor w -text "--"
  label $logcanvas.tdate1 -anchor w -text "--"
  text  $logcanvas.tcomment1 -height 5 -width 75

  label $logcanvas.lvers2 -anchor w -text "Version B"
  label $logcanvas.lwho2 -anchor w -text "Created by"
  label $logcanvas.ldate2 -anchor w -text "Date"
  label $logcanvas.lcomment2 -anchor w -text "Comment"

  entry $logcanvas.tvers2 -relief sunken
  label $logcanvas.twho2 -anchor w -text "--"
  label $logcanvas.tdate2 -anchor w -text "--"
  text  $logcanvas.tcomment2 -height 5 -width 75

  pack $logcanvas.lfname \
    -in $logcanvas.up.left \
    -side top -fill x -pady 2
  pack $logcanvas.tfname \
    -in $logcanvas.up.right \
    -side top -fill x -pady 0
  pack $logcanvas.lvers1 $logcanvas.lwho1 $logcanvas.ldate1 $logcanvas.lcomment1 \
    -in $logcanvas.up1.left \
    -side top -fill x -pady 0
  pack $logcanvas.tvers1 $logcanvas.twho1 $logcanvas.tdate1 $logcanvas.tcomment1 \
    -in $logcanvas.up1.right \
    -side top -fill x -pady 0
  pack $logcanvas.lvers2 $logcanvas.lwho2 $logcanvas.ldate2 $logcanvas.lcomment2 \
    -in $logcanvas.up2.left \
    -side top -fill x -pady 0
  pack $logcanvas.tvers2 $logcanvas.twho2 $logcanvas.tdate2 $logcanvas.tcomment2 \
    -in $logcanvas.up2.right \
    -side top -fill x -pady 0

  canvas $logcanvas.canvas -relief sunken -border 2 \
    -yscrollcommand "$logcanvas.yscroll set" \
    -xscrollcommand "$logcanvas.xscroll set"
  scrollbar $logcanvas.xscroll -relief sunken -orient horizontal \
    -command "$logcanvas.canvas xview"
  scrollbar $logcanvas.yscroll -relief sunken \
    -command "$logcanvas.canvas yview"

  #
  # Create buttons
  #

  button $logcanvas.help -text "Help" \
    -command log_browser
  button $logcanvas.view -text "View" \
    -command "logcanvas_view $logcanvas"
  button $logcanvas.diff -text "Diff" \
    -command "logcanvas_diff $logcanvas"
  pack $logcanvas.help $logcanvas.view $logcanvas.diff \
    -in $logcanvas.down -side left \
    -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both -expand 1
  if {$localfile != "no file"} {
    button $logcanvas.join -text "Merge Branch to Head" \
      -command "logcanvas_join $localfile $logcanvas"
    button $logcanvas.delta -text "Merge Changes to Head" \
      -command "logcanvas_delta $localfile $logcanvas"
    pack $logcanvas.join $logcanvas.delta \
      -in $logcanvas.down -side left \
      -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both -expand 1
  }
  button $logcanvas.viewtags -text "View Tags" \
    -command "nop"
  button $logcanvas.quit -text "Quit" \
    -command "destroy $logcanvas"
  pack $logcanvas.viewtags $logcanvas.quit \
    -in $logcanvas.down -side left \
    -ipadx 2 -ipady 2 -padx 4 -pady 4 -fill both -expand 1

  #
  # Put the canvas on to the display.
  #

  pack $logcanvas.xscroll -side bottom -fill x -padx 2 -pady 2
  pack $logcanvas.yscroll -side right -fill y -padx 2 -pady 2
  pack $logcanvas.canvas -fill both -expand 1

  logcanvas_clear $logcanvas

  #
  # Window manager stuff.
  #

  wm title $logcanvas "CVS Log Browser"
  wm minsize $logcanvas 1 1

  # Draw the log onto the canvas.
  # Start by splitting the log up and parsing it using a simple
  # state machine.

  set loglist [split $filelog "\n"]
  set logstate "searching"
  set logstate "rcsfile"
  foreach logline $loglist {
    switch -exact -- $logstate {
      "rcsfile" {
        # Look for the first text line which should give the file name.
        set fileline [split $logline]
        if {[lindex $fileline 0] == "RCS"} {
          $logcanvas.tfname delete 0 end
          $logcanvas.tfname insert end [lindex $fileline 2]
          set logstate "tags"
          set taglist ""
          if [array exists tagvalues] {
            foreach item [array names tagvalues] {
              set tagvalues($item) ""
            }
          }
          continue
        }
      }
      "tags" {
        # Any line with a tab leader is a tag
        if { [string index $logline 0] == "\t" } {
          set taglist "$taglist$logline\n"
          set tagitems [split $logline ":"]
          set tagname [string trim [lindex $tagitems 0]]
          set tagrevision [string trim [lindex $tagitems 1]]
          set tagvalues($tagname) $tagrevision
          #puts "Tag $tagname is on revision $tagrevision"
        } else {
          if {$logline == "description:"} {
            # No more tags after this point
            $logcanvas.viewtags configure \
              -command "view_output Tags \"$taglist\""
            set logstate "searching"
            continue
          }
          if {$logline == "----------------------------"} {
            # Oops, missed something.
            $logcanvas.viewtags configure \
              -command "view_output Tags \"$taglist\""
            set logstate "revision"
            continue
          }
        }
      }
      "searching" {
        # Look for the line that starts a revision message.
        if {$logline == "----------------------------"} {
          set logstate "revision"
          continue
        }
      }
      "revision" {
        # Look for a revision number line
        set revline [split $logline]
        set revnum [lindex $revline 1]
        set logstate "date"
      }
      "date" {
        # Look for a date line.  This also has the name of the author.
        set dateline [split $logline]
        set revdate [lindex $dateline 1]
        # set revtime [lindex $dateline 2]
        # set revtime [string range $revtime 0 [expr [string length $revtime] - 2]]
        set revwho  [lindex $dateline 5]
        set revwho  [string range $revwho 0 [expr [string length $revwho] - 2]]
        set logstate "logmessage"
        set revcomment ""
      }
      "logmessage" {
        # Read the log message which follows the date line.
        if {$logline == "----------------------------"} {
          set logstate "revision"
          # Draw the revision box on the canvas.
          # puts "Revision number $revnum by $revwho on $revdate at $revtime"
          logcanvas_draw_box $logcanvas $revnum $revwho $revdate $revcomment
          continue
        } elseif {$logline == "============================================================================="} {
          set logstate "terminated"
          # Draw the revision box on the canvas.
          # puts "Revision number $revnum by $revwho on $revdate at $revtime"
          logcanvas_draw_box $logcanvas $revnum $revwho $revdate $revcomment
          continue
        }
        # Process a revision log line
        regsub -all "\"" $logline "'" newline
        set revcomment "$revcomment$newline\n"
      }
      "terminated" {
        # ignore any further lines
        continue
      }
    }
  }

  # End of log file processing.
}

proc logcanvas_clear {logcanvas} {
#
# Clears the canvas and resets globals ready to re-draw.
#
  global cvscanv

  set cvscanv(nextx) $cvscanv(indx)
  set cvscanv(nexty) $cvscanv(indy)
  set cvscanv(indents) 0
  set cvscanv(branches) {}
  set cvscanv(xhigh) [expr $cvscanv(boxx) + $cvscanv(indx)]
  set cvscanv(yhigh) [expr $cvscanv(boxy) + $cvscanv(indy)]
  # $logcanvas.canvas delete all
}

proc logcanvas_box_colour {revnum} {
#
# Determines what colour the box should be.
#
  global cvscfg
  global tagvalues

  set return_colour black

  if [array exists tagvalues] {
    foreach item [array names tagvalues] {
      if [string match $tagvalues($item) $revnum] {
        if [info exists cvscfg(boxcolour,$item)] {
          set return_colour $cvscfg(boxcolour,$item)
        }
      }
    }
  }
  #puts "colour of $revnum is $return_colour"
  return $return_colour
}

proc logcanvas_dot_colour {revnum} {
#
# Determines what colour dots in the box should be.
#
  global cvscfg
  global tagvalues
 
  set return_colour {}
 
  if [array exists tagvalues] {
    foreach item [array names tagvalues] {
      if [string match $tagvalues($item) $revnum] {
        if [info exists cvscfg(dotcolour,$item)] {
          lappend return_colour $cvscfg(dotcolour,$item)
          #puts "Found a dot coloured $cvscfg(dotcolour,$item) on tag $item for revision $revnum"
        }
      }
    }
  }
  return $return_colour
}

proc logcanvas_draw_box {logcanvas revnum revwho revdate revcomment} {
#
# Draws a box containing a revision of a file.
#
  global cvscanv

  set versions [split $revnum "."]
  set numvers [llength $versions]

  switch -exact -- $numvers {
    2 {
      # Remember where the new box is going.
      set cvscanv(posx$revnum) $cvscanv(nextx)
      set cvscanv(posy$revnum) $cvscanv(nexty)
      # draw a box
      logcanvas_rectangle $logcanvas $revnum $revwho $revdate $revcomment \
        $cvscanv(nextx) $cvscanv(nexty)
      # Position the next box.
      set cvscanv(nextx) $cvscanv(indx)
      set cvscanv(nexty) [expr $cvscanv(nexty) + $cvscanv(gapy)]
    }
    4 {
      # Have we seen this branch before?
      set branchid [join [lrange $versions 0 2] "."]
      if {[lsearch -exact $cvscanv(branches) $branchid] == -1} {
        # No
        lappend cvscanv(branches) $branchid
        incr cvscanv(indents)
      }
      # x.y.z.u  -- version x.y must already exist, so find out where it is.
      set backrevnum [join [lrange $versions 0 1] "."]
      set thisx [expr $cvscanv(gapx) * $cvscanv(indents) + \
        $cvscanv(indx) ]
      set thisy [expr $cvscanv(posy$backrevnum) - \
        $cvscanv(gapy) * [lindex $versions 3]]
      # Remember where the new box is going.
      set cvscanv(posx$revnum) $thisx
      set cvscanv(posy$revnum) $thisy
      # Draw the box
      logcanvas_rectangle $logcanvas $revnum $revwho $revdate $revcomment \
        $thisx $thisy
      # Draw a connecting line.
      if {[lindex $versions [expr $numvers - 1]] == "1"} {
        $logcanvas.canvas create line \
          [expr $cvscanv(boxx) / 2 + $thisx] \
          [expr $cvscanv(boxy) + $thisy] \
          [expr $cvscanv(boxx) + $cvscanv(posx$backrevnum)] \
          [expr $cvscanv(boxy) / 2 + $cvscanv(posy$backrevnum)]
      }
    }
    # Yes I know this is ugly -- just a cut and paste of the previous
    # section -- fix this soon.
    6 {
      # Have we seen this branch before?
      set branchid [join [lrange $versions 0 4] "."]
      if {[lsearch -exact $cvscanv(branches) $branchid] == -1} {
        # No
        lappend cvscanv(branches) $branchid
        incr cvscanv(indents)
      }
      # x.y.z.u  -- version x.y must already exist, so find out where it is.
      set backrevnum [join [lrange $versions 0 3] "."]
      set thisx [expr $cvscanv(gapx) * $cvscanv(indents) + \
        $cvscanv(indx) ]
      set thisy [expr $cvscanv(posy$backrevnum) - \
        $cvscanv(gapy) * [lindex $versions 5]]
      # Remember where the new box is going.
      set cvscanv(posx$revnum) $thisx
      set cvscanv(posy$revnum) $thisy
      # Draw the box
      logcanvas_rectangle $logcanvas $revnum $revwho $revdate $revcomment \
        $thisx $thisy
      # Draw a connecting line.
      if {[lindex $versions [expr $numvers - 1]] == "1"} {
        $logcanvas.canvas create line \
          [expr $cvscanv(boxx) / 2 + $thisx] \
          [expr $cvscanv(boxy) + $thisy] \
          [expr $cvscanv(boxx) + $cvscanv(posx$backrevnum)] \
          [expr $cvscanv(boxy) / 2 + $cvscanv(posy$backrevnum)]
      }
    }
  }

}

proc logcanvas_rectangle {logcanvas revnum revwho revdate revcomment x y} {
#
# Breaks out some of the code from the logcanvas_draw_box procedure.
#
  global cvscanv

  set versions [split $revnum "."]
  set numvers [llength $versions]

  # draw a box
  $logcanvas.canvas create rectangle \
    $x $y [expr $x + $cvscanv(boxx)] [expr $y + $cvscanv(boxy)] \
    -width 3 \
    -outline [logcanvas_box_colour $revnum] \
    -tags v$revnum

  # Make sure the scrolling region is big enough
  if {$cvscanv(xlow) > $x} {
    set cvscanv(xlow) $x
  }
  if {$cvscanv(ylow) > $y} {
    set cvscanv(ylow) $y
  }
  if {$cvscanv(xhigh) < [expr $x + $cvscanv(boxx)]} {
    set cvscanv(xhigh) [expr $x + $cvscanv(boxx) + 10]
  }
  if {$cvscanv(yhigh) < [expr $y + $cvscanv(boxy)]} {
    set cvscanv(yhigh) [expr $y + $cvscanv(boxy) + 10]
  }
  $logcanvas.canvas configure \
    -scrollregion "$cvscanv(xlow) $cvscanv(ylow) $cvscanv(xhigh) $cvscanv(yhigh)"

  # Put the version number in the box
  $logcanvas.canvas create text \
    [expr $x + 4] [expr $y + 2] \
    -anchor nw -text $revnum  \
    -fill [logcanvas_box_colour $revnum] \
    -tags v$revnum
  $logcanvas.canvas create text \
    [expr $x + 4] [expr $y + 14] \
    -anchor nw -text $revwho \
    -fill [logcanvas_box_colour $revnum] \
    -tags v$revnum

  # Put some dots in the box if necessary.
  set dotnum 0
  foreach dotcolour [logcanvas_dot_colour $revnum] {
    set dotx [expr $x + $cvscanv(boxx) - 8]
    set doty [expr $dotnum * 6 + $y + 3]
    #puts "Drawing a dot at $dotx $doty coloured $dotcolour"
    $logcanvas.canvas create oval \
      $dotx $doty [expr $dotx + 5] [expr $doty + 5] \
      -fill $dotcolour \
      -outline ""
    incr dotnum
  }
      
  # Bind to the tag.
  $logcanvas.canvas bind v$revnum <ButtonPress-1> \
    "$logcanvas.tvers1 delete 0 end
     $logcanvas.tvers1 insert end $revnum
     $logcanvas.twho1 configure -text $revwho
     $logcanvas.tdate1 configure -text $revdate
     $logcanvas.tcomment1 delete 1.0 end
     $logcanvas.tcomment1 insert end \"$revcomment\""
  $logcanvas.canvas bind v$revnum <ButtonPress-3> \
    "$logcanvas.tvers2 delete 0 end
     $logcanvas.tvers2 insert end $revnum
     $logcanvas.twho2 configure -text $revwho
     $logcanvas.tdate2 configure -text $revdate
     $logcanvas.tcomment2 delete 1.0 end
     $logcanvas.tcomment2 insert end \"$revcomment\""
  # Draw a connecting line.  Up if X.0, nowhere if X.1, down if > X.1
  # This should work for most version starting points.
  if {[lindex $versions [expr $numvers - 1]] == "0"} {
    $logcanvas.canvas create line \
      [expr $cvscanv(boxx) / 2 + $x] \
      [expr $y - $cvscanv(gapy) + $cvscanv(boxy)] \
      [expr $cvscanv(boxx) / 2 + $x] \
      [expr $y]
  } elseif {[lindex $versions [expr $numvers - 1]] != "1"} {
    $logcanvas.canvas create line \
      [expr $cvscanv(boxx) / 2 + $x] \
      [expr $cvscanv(boxy) + $y] \
      [expr $cvscanv(boxx) / 2 + $x] \
      [expr $cvscanv(gapy) + $y]
  }
}

proc logcanvas_view {logcanvas} {
#
# Views the selected version.
#
  set ver1 [$logcanvas.tvers1 get]
  set localfile [$logcanvas.tlocalfile get]

  if {$localfile != "no file"} {
    cvs_view_r $ver1 $localfile
  } else {
    set fname [$logcanvas.tfname get]
    rcs_fileview $fname $ver1
  }
}

proc logcanvas_diff {logcanvas} {
#
# Diffs two versions.
#
  set ver1 [$logcanvas.tvers1 get]
  set ver2 [$logcanvas.tvers2 get]
  set localfile [$logcanvas.tlocalfile get]

  if {$localfile != "no file"} {
    cvs_diff_r $ver1 $ver2 $localfile
  } else {
    set fname [$logcanvas.tfname get]
    rcs_filediff $fname $ver1 $ver2
  }
}

proc logcanvas_join {localfile logcanvas} {
#
# Joins a branch version to the head version.
#
  set ver1 [$logcanvas.tvers1 get]
  set versions [split $ver1 "."]
  set numvers [llength $versions]
  if {$numvers < 4} {
    cvserror "Please select a branch version for this function!"
    return 1
  }

  cvs_join $localfile $ver1
}

proc logcanvas_delta {localfile logcanvas} {
#
# Merges changes in the delta between two versions to the head
# version.
#
  set ver1 [$logcanvas.tvers1 get]
  set ver2 [$logcanvas.tvers2 get]

  cvs_delta $localfile $ver1 $ver2
}
