1#
2# Tcl Library for TkCVS
3#
4
5namespace eval joincanvas {
6  variable instance 0
7
8  proc new {localfile filelog {current_tagname {}}} {
9    variable instance
10    set my_idx $instance
11    incr instance
12
13    if {[catch "image type Modules"]} {
14      workdir_images
15    }
16    if {[catch "image type Workdir"]} {
17      modbrowse_images
18    }
19
20    #
21    # Creates a new log canvas.  filelog must be the output of a cvs
22    # log or rlog command.
23    #
24    namespace eval $my_idx {
25      set my_idx [uplevel {concat $my_idx}]
26      set filelog [uplevel {concat $filelog}]
27      variable localfile [uplevel {concat $localfile}]
28      variable current_tagname [uplevel {concat $current_tagname}]
29
30      global cvscfg
31      global cvsglb
32      global cvs
33      global tcl_platform
34
35      # Height and width to draw boxes
36      variable cvscanv
37      set cvscanv(boxx) 60
38      set cvscanv(boxy) 20
39      set cvscanv(midx) [expr {$cvscanv(boxx) / 2}]
40      set cvscanv(midy) [expr {$cvscanv(boxy) / 2}]
41      set cvscanv(boxmin) 64
42      # Gaps between boxes
43      set cvscanv(space) [expr {$cvscanv(boxy) + 16}]
44      # Indent at top left of canvas
45      set cvscanv(indx) 5
46      set cvscanv(indy) 5
47      # Static type variables used while drawing on the canvas.
48      set cvscanv(xhigh) 0
49      set cvscanv(yhigh) 0
50      set cvscanv(xlow)  0
51      set cvscanv(ylow)  0
52
53      variable revlist
54      variable revbranches
55      variable tags
56      variable headrev
57      variable joincanvas
58
59      set joincanvas ".joincanvas$my_idx"
60
61      proc parse_cvslog_tags {filelog} {
62        variable joincanvas
63        variable tags
64        variable headrev
65
66        gen_log:log T "ENTER ($joincanvas ...)"
67        set loglist [split $filelog "\n"]
68        set logstate "rcsfile"
69        foreach logline $loglist {
70          #puts "$logline"
71          switch -exact -- $logstate {
72            "rcsfile" {
73              # Look for the first text line which should give the file name.
74              set fileline [split $logline]
75              if {[lindex $fileline 0] == "RCS"} {
76                set logstate "head"
77                continue
78              }
79            }
80            "head" {
81              set fileline [split $logline]
82              if {[lindex $fileline 0] == "head:"} {
83                set headrev [lindex $fileline 1]
84                set logstate "tags"
85                set taglist ""
86                continue
87              }
88            }
89            "tags" {
90              # Any line with a tab leader is a tag
91              if { [string index $logline 0] == "\t" } {
92                set taglist "$taglist$logline\n"
93                set tagitems [split $logline ":"]
94                set tagrevision [string trim [lindex $tagitems 1]]
95                set tagname [string trim [lindex $tagitems 0]]
96                # Add all the tags to a picklist for our "since" tag
97                ::picklist::used alltags $tagname
98
99                set parts [split $tagrevision {.}]
100                if {[expr {[llength $parts] & 1}] == 1} {
101                  set parts [linsert $parts end-1 {0}]
102                  set tagrevision [join $parts {.}]
103                }
104                # But we only want to know the branch tags
105                if { [regexp {\.0\.\d+$} $tagrevision] } {
106                  set tagstring [string trim [lindex $tagitems 0]]
107                  lappend tags($tagrevision) $tagstring
108                }
109              } else {
110                if {$logline == "description:"} {
111                  # No more tags after this point
112                  set logstate "searching"
113                  continue
114                }
115                if {$logline == "----------------------------"} {
116                  # Oops, missed something.
117                  set logstate "revision"
118                  continue
119                }
120              }
121            }
122            "terminated" {
123              # ignore any further lines
124              continue
125            }
126          }
127        }
128        ::picklist::used alltags ""
129      }
130
131      proc node {joincanvas rev x y} {
132        global cvscfg
133        variable cvscanv
134        variable tags
135        upvar treelist treelist
136        upvar ylevel ylevel
137        upvar ind ind
138
139        gen_log:log T "ENTER ($rev $x $y)"
140        $joincanvas.canvas create line \
141          $x [expr {$y + $cvscanv(boxy)}] \
142          $x [expr {$y + $cvscanv(space)}]
143
144        gen_log:log T "LEAVE"
145      }
146
147      proc rectangle {joincanvas rev x y} {
148        #
149        # Breaks out some of the code from the joincanvas_draw_box procedure.
150        # Work out the width of the text to go in the box first, then draw a
151        # box wide enough.
152        #
153        global cvscfg
154        variable cvscanv
155        variable tags
156        variable current_tagname
157        upvar x xpos
158
159        gen_log:log T "ENTER ($rev $x $y)"
160
161        set parts [split $rev "."]
162
163        set tagtext $tags($rev)
164        gen_log:log D "$tagtext\t$rev"
165        $joincanvas.canvas create text \
166           [expr {$x + 4}] [expr {$y + 2}] \
167           -text "$tagtext" \
168           -anchor nw -fill blue \
169           -font {Helvetica -12 bold} \
170           -tags b$rev
171
172        set tagwidth [font measure {Helvetica -12 bold} \
173           -displayof $joincanvas.canvas $tagtext]
174        if {$tagwidth < $cvscanv(boxmin)} { set tagwidth $cvscanv(boxmin) }
175
176        # draw the box
177        set boxid [$joincanvas.canvas create rectangle \
178          $x $y \
179          [expr {$x + $tagwidth + 5}] [expr {$y + $cvscanv(boxy)}] \
180          -width 3 \
181          -fill gray90 \
182          -tags [list b$rev rect$rev] \
183        ]
184        # Drop the fill color below the text so the text isn't hidden
185        $joincanvas.canvas lower $boxid
186
187        # Bind button-presses to the rectangles.
188        if {$tags($rev) != ""} {
189        $joincanvas.canvas bind b$rev <ButtonPress-1> \
190           [namespace code "select_rectangle $rev $tags($rev)"]
191        }
192
193        if {"$current_tagname" == "$tagtext"} {
194          you_are_here $rev $tagwidth $x $y
195        }
196        gen_log:log T "LEAVE"
197      }
198
199      proc unselect_all {} {
200        variable joincanvas
201        set t [$joincanvas.canvas gettags current]
202        if {$t != {} } {return}
203        unselect_rectangle
204      }
205
206      proc unselect_rectangle {} {
207        variable joincanvas
208        catch {$joincanvas.canvas itemconfigure SelA -fill gray90}
209        $joincanvas.up.rversFrom delete 0 end
210        $joincanvas.canvas dtag SelA
211      }
212
213      proc select_rectangle {rev tags} {
214        global cvscfg
215        variable joincanvas
216
217        gen_log:log T "ENTER ($rev $tags)"
218
219        unselect_rectangle
220        $joincanvas.up.rversFrom delete 0 end
221        $joincanvas.up.rversFrom insert end $tags
222        $joincanvas.canvas addtag SelA withtag rect$rev
223        $joincanvas.canvas itemconfigure SelA -fill $cvscfg(colourA)
224      }
225
226      proc fillcanvas {filename filelog} {
227        global cvscfg
228        variable joincanvas
229        variable cvscanv
230        variable headrev
231        variable tags
232        variable current_tagname
233
234        gen_log:log T "ENTER ($filename <filelog suppressed>)"
235
236        catch {unset tags}
237        # Collect the history from the RCS log
238        $joincanvas.canvas delete all
239        parse_cvslog_tags $filelog
240
241        # Sort the branch revisions
242        set tagrevlist [lsort -command sortrevs [array names tags]]
243        # Get rid of duplicates
244        set revlist ""
245        foreach t $tagrevlist {
246          if {[lsearch -exact $revlist $t] < 0} {
247            lappend revlist $t
248          }
249        }
250
251        # Find everybody's parents.  Add parent nodes to a new nodelist.
252        # Keep track of everybody's children
253        set treelist ""
254        foreach rev $revlist {
255          gen_log:log D "$rev"
256          # Find its parent
257          set alist [split $rev "."]
258          set alength [llength $alist]
259          set isodd [expr {$alength % 2}]
260          set parent($rev) [join [lrange $alist 0 [expr {$alength - 3}]] "."]
261          #gen_log:log D " parent $parent($rev)"
262          set parentbranch [join [lrange $alist 0 [expr {$alength - 5}]] "."]
263          #gen_log:log D " parentbrancch $parentbranch"
264          set branchnum       [lindex $alist [expr {$alength - 4}]]
265          set branchparent [join [list $parentbranch 0 $branchnum] "."]
266          #gen_log:log D " branchparent $branchparent"
267          if {$isodd > 0} {
268            set parent($rev) [join [lrange $alist 0 [expr {$alength - 2}]] "."]
269            #gen_log:log D " parent $parent($rev)"
270          }
271          if {[string length $parentbranch] > 0} {
272          gen_log:log D "set parent parent($rev)"
273            set parent($rev) $branchparent
274            lappend children($branchparent) $rev
275          } else {
276            lappend children($parent($rev)) $rev
277          }
278          # Add to new list of nodes
279          if {[lsearch -exact $revlist $parent($rev)] < 0 && \
280              [lsearch -exact $treelist $parent($rev)] < 0 } {
281            lappend treelist $parent($rev)
282            gen_log:log D " add parent $parent($rev) of $rev"
283          }
284        }
285        # Do it all over again for the new ones we added
286        foreach rev $treelist {
287          gen_log:log D "new $rev"
288          # Find its parent
289          set alist [split $rev "."]
290          set alength [llength $alist]
291          set isodd [expr {$alength % 2}]
292          set parent($rev) [join [lrange $alist 0 [expr {$alength - 3}]] "."]
293          #gen_log:log D " parent $parent($rev)"
294          set parentbranch [join [lrange $alist 0 [expr {$alength - 5}]] "."]
295          #gen_log:log D " parentbrancch $parentbranch"
296          set branchnum       [lindex $alist [expr {$alength - 4}]]
297          set branchparent [join [list $parentbranch 0 $branchnum] "."]
298          #gen_log:log D " branchparent $branchparent"
299          if {$isodd > 0} {
300            set parent($rev) [join [lrange $alist 0 [expr {$alength - 2}]] "."]
301            #gen_log:log D " parent $parent($rev)"
302          }
303          if {[string length $parentbranch] > 0} {
304          gen_log:log D "set parent parent($rev)"
305            set parent($rev) $branchparent
306            lappend children($branchparent) $rev
307          } else {
308            lappend children($parent($rev)) $rev
309          }
310        }
311        set treelist [concat $revlist $treelist]
312        set treelist [lsort -command sortrevs $treelist]
313
314        # Now prepare to draw the revision tree
315        # Root first
316        set y $cvscanv(space)
317        set px(0) 10
318        set x [font measure {Helvetica -12 bold} \
319           -displayof $joincanvas.canvas $cvscfg(mergetrunkname)]
320
321        set px(1) [expr {$px(0) + $x / 2}]
322        set py(1) [expr {$cvscanv(boxy) - 4}]
323
324        $joincanvas.canvas create text \
325           $px(1) $y \
326           -text "ROOT" \
327           -anchor n -fill black \
328           -font {Helvetica -12 bold}
329
330        # Then the rest
331        foreach rev $treelist {
332          gen_log:log D "$rev"
333          if {[info exists children($rev)]} {
334            foreach r $children($rev) {
335              gen_log:log D "\tparent of $r"
336            }
337            set nchildren($rev) [llength $children($rev)]
338            set kids [array names children $rev.*]
339            foreach kid $kids {
340              set descendents $children($kid)
341              set ndescendents [llength $descendents]
342              gen_log:log D "\tgranchildren: $descendents"
343              incr nchildren($rev) $ndescendents
344            }
345          } else {
346            set nchildren($rev) 0
347          }
348          gen_log:log D "\t$nchildren($rev) descendents"
349          if {[info exists parent($rev)]} {
350            gen_log:log D "\tchild of $parent($rev)"
351          }
352
353          set alist [split $rev "."]
354          set alength [llength $alist]
355          # Round up instead of down
356          set ind [expr {($alength +1)/ 2}]
357          set pind [expr {$ind - 1}]
358
359          if {! [info exists py($ind)]} {
360            gen_log:log D "  starting new column $ind"
361            set py($ind) $cvscanv(space)
362            set px($ind) [expr {$px($pind) + $cvscanv(midx) + $cvscanv(space)}]
363          }
364          if {[info exists parent($rev)] && $parent($rev) != ""} {
365            gen_log:log D "  this one has a parent in col >=1"
366            if {[info exists ylevel($parent($rev))] && $py($ind) > $ylevel($parent($rev))} {
367              gen_log:log D "  jumping to level of parent"
368              set py($ind) $ylevel($parent($rev))
369              if {$ind > 2} {
370                # Give it a node if its parent isn't in column1
371                incr ylevel($parent($rev)) -$cvscanv(space)
372                set px($ind) [expr {$px($pind) + $cvscanv(boxx) + $cvscanv(space)}]
373                set py($ind) $ylevel($parent($rev))
374                node $joincanvas $rev \
375                  [expr {$px($pind) + $cvscanv(midx)}] \
376                  [expr {$py($ind) - 1}]
377              }
378            } else {
379              gen_log:log D "  parent not higher"
380              set py($ind) [expr {$py($ind) - $cvscanv(space)}]
381            }
382            set xlevel($rev) [expr {$px($ind) + $cvscanv(midx)}]
383          } else {
384            set py($ind) [expr {$py($ind) - $cvscanv(space)}]
385            gen_log:log D "  just stacking it above the last one"
386            set xlevel($rev) $px($ind)
387          }
388          set ylevel($rev) $py($ind)
389
390          # For column 1, just draw a nondescript node
391          if {$ind == 1} {
392            #node $joincanvas $rev $px($ind) $py($ind)
393            set py($ind) [expr {$py($ind) - ($nchildren($rev) - 1) * $cvscanv(space)}]
394          } else {
395            if {! [info exists tags($rev)]} {
396              set tags($rev) ""
397            }
398            gen_log:log D "  tag:  $tags($rev)"
399            rectangle $joincanvas $rev $px($ind) $py($ind)
400            # Line linking it to parent
401            if {$ind > 2} {
402               set ly [expr {$ylevel($parent($rev)) + $cvscanv(midy)}]
403            } else {
404               set ly [expr {$py($ind) + $cvscanv(midy)}]
405            }
406            if {![info exists xlevel($parent($rev))]} {set xlevel($parent($rev)) $px([expr $ind-1])}
407            $joincanvas.canvas create line \
408              $xlevel($parent($rev)) [expr {$ly + 10}] \
409              [expr {$xlevel($parent($rev)) + 10}] $ly \
410              $px($ind) [expr {$py($ind) + $cvscanv(midy)}]
411            set py($ind) [expr {$py($ind) - $nchildren($rev) * $cvscanv(space)}]
412          }
413        }
414
415        set py(1) [expr {$cvscanv(boxy) - 4}]
416        set maxyind 0
417        foreach i [array names py] {
418          if {$py($i) < $maxyind} {
419            set maxyind $py($i)
420          }
421        }
422
423        set tags($headrev) $cvscfg(mergetrunkname)
424        gen_log:log D "HEAD  $headrev"
425        gen_log:log D "tagtext \"$tags($headrev)\""
426        # Make a box for top of trunk
427        set ylevel(trunk) [expr {$maxyind - $cvscanv(boxy)}]
428        set tagwidth [font measure {Helvetica -12 bold} \
429           -displayof $joincanvas.canvas $cvscfg(mergetrunkname)]
430        if {$tagwidth < $cvscanv(boxmin)} { set tagwidth $cvscanv(boxmin) }
431        set boxid [$joincanvas.canvas create rectangle \
432          [expr {$px(1) - $tagwidth / 2}] $ylevel(trunk) \
433          [expr {$px(1) + 5 + $tagwidth / 2}] \
434          [expr {$ylevel(trunk) - $cvscanv(boxy)}] \
435          -width 3 \
436          -fill gray90 \
437          -tags b$headrev]
438        $joincanvas.canvas lower $boxid
439        $joincanvas.canvas create text \
440           [expr {$px(1) + 2}] [expr {$ylevel(trunk) - 2}] \
441           -text "$cvscfg(mergetrunkname)" \
442           -anchor s -justify center -fill blue \
443           -font {Helvetica -12 bold} \
444           -tags b$headrev
445        # Bottom then top
446        $joincanvas.canvas create line \
447           $px(1) [expr {$cvscanv(space) - 4}] \
448           $px(1) $ylevel(trunk)
449
450        # Bind button-press
451        $joincanvas.canvas bind b$headrev <ButtonPress-1> \
452           [namespace code "select_rectangle $headrev $cvscfg(mergetrunkname)"]
453        # Clicking in a blank part of the canvas unselects boxes
454        bind $joincanvas.canvas <ButtonPress-1> \
455           [namespace code unselect_all]
456
457
458        # You are Here
459        if {$current_tagname == "trunk"} {
460          you_are_here $headrev $tagwidth \
461            [expr {$px(1) - $tagwidth / 2 }] \
462            [expr {$ylevel(trunk) - $cvscanv(boxy)}]
463        }
464
465        # now calculate the bounding box using the canvas bbox function
466        set bbox [$joincanvas.canvas bbox all]
467        set boty [lindex $bbox 1]
468        set topy [lindex $bbox 3]
469        set bheight [expr {$topy - $boty}]
470
471        set origheight [lindex [$joincanvas.canvas config -height] 4]
472
473        set screenHeight [winfo vrootheight .]
474        if {$bheight > $screenHeight} {
475          set bheight $screenHeight
476        }
477        if {$bheight > $origheight} {
478          $joincanvas.canvas config -height $bheight
479        }
480
481        $joincanvas.canvas config -scrollregion $bbox
482        $joincanvas.canvas yview moveto 0
483
484        set here [$joincanvas.up.rversTo get]
485        if {$here == ""} {
486          cvsfail "I can't find where I am.  Perhaps the working directory isn't at the head of a branch?" $joincanvas
487        }
488        gen_log:log T "LEAVE"
489      }
490
491      proc you_are_here {rev offset hx hy} {
492        variable cvscanv
493        variable joincanvas
494        variable tags
495
496        gen_log:log T "ENTER ($rev $offset $hx $hy)"
497        gen_log:log D "tags($rev) $tags($rev)"
498        $joincanvas.canvas create image \
499          [expr {$hx + $offset + 16}] [expr {$hy + $cvscanv(boxy)}] \
500          -image Man -anchor s \
501          -tag you_are_here_icon
502        $joincanvas.canvas create text \
503          [expr {$hx + $offset + 26}] [expr {$hy + $cvscanv(boxy)}] \
504          -text "You are\nhere" -anchor sw \
505          -fill red3 \
506          -font {Helvetica -10 bold} \
507          -tag you_are_here_icon
508
509        # Put the name in the "To" entry and disable it.  You can only
510        # merge to where you are.
511        $joincanvas.up.rversTo configure -state normal
512        $joincanvas.up.rversTo delete 0 end
513        $joincanvas.up.rversTo insert end $tags($rev)
514        $joincanvas.up.rversTo configure -state readonly
515        $joincanvas.canvas bind b$rev <ButtonPress-1> {}
516      }
517
518      toplevel $joincanvas
519      wm title $joincanvas "CVS Directory Merge"
520      if {$tcl_platform(platform) != "windows"} {
521        wm iconbitmap $joincanvas @$cvscfg(bitmapdir)/dirbranch.xbm
522      }
523      wm protocol $joincanvas WM_DELETE_WINDOW \
524        [namespace code {$joincanvas.close invoke}]
525
526      $joincanvas configure -menu $joincanvas.menubar
527      menu $joincanvas.menubar
528
529      $joincanvas.menubar add cascade -label "File" \
530        -menu $joincanvas.menubar.file -underline 0
531      menu $joincanvas.menubar.file -tearoff 0
532      $joincanvas.menubar.file add command -label "Close" -underline 0 \
533        -command [namespace code {$joincanvas.close invoke}]
534      $joincanvas.menubar.file add command -label "Exit" -underline 1 \
535        -command { exit_cleanup 1 }
536
537      $joincanvas.menubar add cascade -label "Help" \
538        -menu $joincanvas.menubar.help -underline 0
539      menu $joincanvas.menubar.help -tearoff 0
540      $joincanvas.menubar.help add command -label "Merge Tool" -underline 0 \
541        -command directory_branch_viewer
542
543      frame $joincanvas.up -relief groove -border 2
544      pack $joincanvas.up -side top -fill x
545
546      button $joincanvas.up.bworkdir -image Workdir \
547       -command { workdir_setup }
548      button $joincanvas.up.bmodbrowse -image Modules_cvs \
549       -command { modbrowse_run cvs }
550
551      label $joincanvas.up.lfname -text "Representative File" -anchor w
552      entry $joincanvas.up.rfname -textvariable [namespace current]::repfile
553      bind $joincanvas.up.rfname <Return> \
554        [namespace code {join_getlog $repfile [namespace current]}]
555
556      label $joincanvas.up.lversFrom -text "Merge From" -anchor w
557      frame $joincanvas.up.eFrom -bg $cvscfg(colourA)
558      entry $joincanvas.up.rversFrom
559
560      label $joincanvas.up.lversSince -text "   Since" -anchor w
561      frame $joincanvas.up.eSince -bg $cvscfg(colourB)
562      ::picklist::clear alltags
563      ::picklist::entry $joincanvas.up.rversSince "" alltags
564      label $joincanvas.up.lversTo -text "Merge To" -anchor w
565      entry $joincanvas.up.rversTo -relief groove \
566        -bd 1 -relief sunk -state readonly -readonlybackground $cvsglb(bg)
567
568      grid columnconf $joincanvas.up 1 -weight 1
569      grid rowconf $joincanvas.up 3 -weight 1
570      grid $joincanvas.up.lfname -column 0 -row 0 -sticky w
571      grid $joincanvas.up.rfname -column 1 -row 0 -padx 3 -sticky ew
572      grid $joincanvas.up.bworkdir -column 2 -row 0 -rowspan 2 \
573        -sticky e -padx 2 -pady 1
574      grid $joincanvas.up.lversFrom -column 0 -row 1 -sticky w
575      grid $joincanvas.up.eFrom -column 1 -row 1 -sticky ew -padx 4
576      grid $joincanvas.up.bmodbrowse -column 2 -row 2 -rowspan 2 \
577        -sticky e -padx 2 -pady 1
578      grid $joincanvas.up.lversSince -column 0 -row 2 -sticky w
579      grid $joincanvas.up.eSince -column 1 -row 2 -sticky ew -padx 4
580      grid $joincanvas.up.lversTo -column 0 -row 3 -sticky w
581      grid $joincanvas.up.rversTo -column 1 -row 3 -padx 3 -sticky ew
582
583      pack $joincanvas.up.rversFrom -in $joincanvas.up.eFrom \
584        -padx 2 -pady 2 -fill x
585      pack $joincanvas.up.rversSince -in $joincanvas.up.eSince \
586        -padx 2 -pady 2 -fill x
587
588      set textfont [$joincanvas.up.rfname cget -font]
589
590      # Pack the bottom before the middle so it doesnt disappear if
591      # the window is resized smaller
592      frame $joincanvas.down -relief groove -border 2
593      pack $joincanvas.down -side bottom -fill x
594
595      set repfile $localfile
596
597      # The canvas for the big picture
598      canvas $joincanvas.canvas -relief sunken -border 2 \
599        -yscrollcommand "$joincanvas.yscroll set" \
600        -xscrollcommand "$joincanvas.xscroll set"
601      scrollbar $joincanvas.xscroll -relief sunken -orient horizontal \
602        -command "$joincanvas.canvas xview"
603      scrollbar $joincanvas.yscroll -relief sunken \
604        -command "$joincanvas.canvas yview"
605
606      #
607      # Create buttons
608      #
609      button $joincanvas.delta -image Mergediff \
610          -command [namespace code {
611                 set fromrev [$joincanvas.up.rversFrom get]
612                 if {$fromrev == ""} {
613                   cvsfail "Please select a branch!" $joincanvas; return
614                 }
615                 set sincerev [$joincanvas.up.rversSince.e get]
616                 cvs_merge $joincanvas $fromrev $sincerev $fromrev .
617                 }]
618
619      button $joincanvas.down.blogfile -image Branches \
620         -command "cvs_branches $repfile"
621      frame $joincanvas.down.btnfm
622      frame $joincanvas.down.closefm -relief groove -bd 2
623      button $joincanvas.close -text "Close" \
624        -command [namespace code "
625                   destroy $joincanvas
626                   namespace delete [namespace current]
627                   exit_cleanup 0
628                 "]
629
630      pack $joincanvas.down.blogfile -side left \
631        -ipadx 4 -ipady 4
632      pack $joincanvas.down.btnfm -side left -fill y -expand 1
633      pack $joincanvas.delta \
634        -in $joincanvas.down.btnfm -side left \
635        -ipadx 4 -ipady 4
636      pack $joincanvas.down.closefm -side right
637      pack $joincanvas.close \
638        -in $joincanvas.down.closefm -side right \
639        -fill both -expand 1
640
641      set_tooltips $joincanvas.down.blogfile \
642         {"Revision Log and Branch Diagram of the current file"}
643      set_tooltips $joincanvas.delta \
644         {"Merge to current"}
645      set_tooltips $joincanvas.up.bworkdir \
646        {"Open the Working Directory Browser"}
647      set_tooltips $joincanvas.up.bmodbrowse \
648        {"Open the Repository Browser"}
649
650      #
651      # Put the canvas on to the display.
652      #
653      pack $joincanvas.xscroll -side bottom -fill x -padx 1 -pady 1
654      pack $joincanvas.yscroll -side right -fill y -padx 1 -pady 1
655      pack $joincanvas.canvas -fill both -expand 1
656
657      $joincanvas.canvas delete all
658
659      #
660      # Window manager stuff.
661      #
662      wm minsize $joincanvas 1 1
663
664      scrollbindings Canvas
665      focus $joincanvas.canvas
666
667      fillcanvas $localfile $filelog
668
669      return [namespace current]
670    }
671  }
672}
673
674proc cvs_joincanvas { } {
675# Find the bushiest file in the directory and diagram it
676  global cvs
677  global incvs
678  global cvscfg
679  global current_tagname
680
681  gen_log:log T "ENTER"
682  if {! $incvs} {
683    cvs_notincvs
684    return 1
685  }
686  set files [glob -nocomplain -types f -- .??* *]
687
688  regsub -all {\$} $files {\$} files
689  set commandline "$cvs -d $cvscfg(cvsroot) log $files"
690  gen_log:log C "$commandline"
691  catch {eval "exec $commandline"} raw_log
692  set log_lines [split $raw_log "\n"]
693
694  gen_log:log D "Directory tag: $current_tagname"
695  foreach logline $log_lines {
696    if {[string match "Working file:*" $logline]} {
697      set filename [lrange [split $logline] 2 end]
698      set nbranches($filename) 0
699      continue
700    }
701    if {[string match "total revisions:*" $logline]} {
702      set nrevs($filename) [lindex [split $logline] end]
703      continue
704    }
705    if { [regexp {^\t[-\w]+: .*\.0\.\d+$} $logline] } {
706      incr nbranches($filename)
707    }
708  }
709  set bushiestfile ""
710  set mostrevisedfile ""
711  set nbrmax 0
712  foreach br [array names nbranches] {
713    if {$nbranches($br) > $nbrmax} {
714      set bushiestfile $br
715      set nbrmax $nbranches($br)
716    }
717  }
718  set nrevmax 0
719  foreach br [array names nrevs] {
720    if {$nrevs($br) > $nrevmax} {
721      set mostrevisedfile $br
722      set nrevmax $nrevs($br)
723    }
724  }
725  gen_log:log F "Bushiest file \"$bushiestfile\" has $nbrmax branches"
726  gen_log:log F "Most Revised file \"$mostrevisedfile\" has $nrevmax revisions"
727
728  # Sometimes we don't find a file with any branches at all, so bushiest
729  # is empty.  Fall back to mostrevised.  All files have at least one rev.
730  if {[string length $bushiestfile] > 0} {
731    join_getlog $bushiestfile
732  } else {
733    join_getlog $mostrevisedfile
734  }
735
736  gen_log:log T "LEAVE"
737}
738
739# Get the file log.  Make a new canvas or re-draw an existing one.
740proc join_getlog {filename {name_idx {}}} {
741  global cvscfg
742  global cvs
743  global current_tagname
744
745  gen_log:log T "ENTER ($filename $name_idx)"
746  set commandline "$cvs -d $cvscfg(cvsroot) log \"$filename\""
747  gen_log:log C "$commandline"
748  set ret [catch {eval "exec $commandline"} view_this]
749  # If you bail, sometimes you discard a perfectly good log
750  #if {$ret} {
751    #cvsfail $view_this
752    #gen_log:log T "LEAVE ERROR ($view_this)"
753    #return
754  #}
755  if {$name_idx == ""} {
756    joincanvas::new $filename $view_this $current_tagname
757  } else {
758    $name_idx\::fillcanvas $filename $view_this
759  }
760  gen_log:log T "LEAVE"
761}
762