git-gui: Convert browser, console to "class" format

Now that we have a slightly easier method of working with per-widget
data we should make use of that technique in our browser and console
meta-widgets, as both have a decent amount of information that they
store on a per-widget basis and our current approach of handling
it is difficult to follow.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
diff --git a/git-gui.sh b/git-gui.sh
index 84a3b62..67f993a 100755
--- a/git-gui.sh
+++ b/git-gui.sh
@@ -1288,7 +1288,7 @@
 
 .mbar.repository add command \
 	-label {Browse Current Branch} \
-	-command {new_browser $current_branch}
+	-command {browser::new $current_branch}
 trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#"
 .mbar.repository add separator
 
@@ -1581,7 +1581,7 @@
 		exit 1
 	}
 	set current_branch [lindex $argv 0]
-	new_browser $current_branch
+	browser::new $current_branch
 	return
 }
 blame {
diff --git a/lib/blame.tcl b/lib/blame.tcl
index 6d894e5..dc7b22f 100644
--- a/lib/blame.tcl
+++ b/lib/blame.tcl
@@ -1,6 +1,8 @@
 # git-gui blame viewer
 # Copyright (C) 2006, 2007 Shawn Pearce
 
+set next_browser_id 0
+
 proc show_blame {commit path} {
 	global next_browser_id blame_status blame_data
 
diff --git a/lib/browser.tcl b/lib/browser.tcl
index 631859a..f9a4e1d 100644
--- a/lib/browser.tcl
+++ b/lib/browser.tcl
@@ -1,28 +1,26 @@
 # git-gui tree browser
 # Copyright (C) 2006, 2007 Shawn Pearce
 
-set next_browser_id 0
+class browser {
 
-proc new_browser {commit} {
-	global next_browser_id cursor_ptr M1B
-	global browser_commit browser_status browser_stack browser_path browser_busy
+field w
+field browser_commit
+field browser_path
+field browser_files  {}
+field browser_status {Starting...}
+field browser_stack  {}
+field browser_busy   1
 
-	if {[winfo ismapped .]} {
-		set w .browser[incr next_browser_id]
-		set tl $w
-		toplevel $w
-	} else {
-		set w {}
-		set tl .
-	}
-	set w_list $w.list.l
-	set browser_commit($w_list) $commit
-	set browser_status($w_list) {Starting...}
-	set browser_stack($w_list) {}
-	set browser_path($w_list) $browser_commit($w_list):
-	set browser_busy($w_list) 1
+constructor new {commit} {
+	global cursor_ptr M1B
+	make_toplevel top w
+	wm title $top "[appname] ([reponame]): File Browser"
 
-	label $w.path -textvariable browser_path($w_list) \
+	set browser_commit $commit
+	set browser_path $browser_commit:
+
+	label $w.path \
+		-textvariable @browser_path \
 		-anchor w \
 		-justify left \
 		-borderwidth 1 \
@@ -31,6 +29,7 @@
 	pack $w.path -anchor w -side top -fill x
 
 	frame $w.list
+	set w_list $w.list.l
 	text $w_list -background white -borderwidth 0 \
 		-cursor $cursor_ptr \
 		-state disabled \
@@ -49,176 +48,149 @@
 	pack $w_list -side left -fill both -expand 1
 	pack $w.list -side top -fill both -expand 1
 
-	label $w.status -textvariable browser_status($w_list) \
+	label $w.status \
+		-textvariable @browser_status \
 		-anchor w \
 		-justify left \
 		-borderwidth 1 \
 		-relief sunken
 	pack $w.status -anchor w -side bottom -fill x
 
-	bind $w_list <Button-1>        "browser_click 0 $w_list @%x,%y;break"
-	bind $w_list <Double-Button-1> "browser_click 1 $w_list @%x,%y;break"
-	bind $w_list <$M1B-Up>         "browser_parent $w_list;break"
-	bind $w_list <$M1B-Left>       "browser_parent $w_list;break"
-	bind $w_list <Up>              "browser_move -1 $w_list;break"
-	bind $w_list <Down>            "browser_move 1 $w_list;break"
-	bind $w_list <$M1B-Right>      "browser_enter $w_list;break"
-	bind $w_list <Return>          "browser_enter $w_list;break"
-	bind $w_list <Prior>           "browser_page -1 $w_list;break"
-	bind $w_list <Next>            "browser_page 1 $w_list;break"
+	bind $w_list <Button-1>        "[cb _click 0 @%x,%y];break"
+	bind $w_list <Double-Button-1> "[cb _click 1 @%x,%y];break"
+	bind $w_list <$M1B-Up>         "[cb _parent]        ;break"
+	bind $w_list <$M1B-Left>       "[cb _parent]        ;break"
+	bind $w_list <Up>              "[cb _move -1]       ;break"
+	bind $w_list <Down>            "[cb _move  1]       ;break"
+	bind $w_list <$M1B-Right>      "[cb _enter]         ;break"
+	bind $w_list <Return>          "[cb _enter]         ;break"
+	bind $w_list <Prior>           "[cb _page -1]       ;break"
+	bind $w_list <Next>            "[cb _page  1]       ;break"
 	bind $w_list <Left>            break
 	bind $w_list <Right>           break
 
-	bind $tl <Visibility> "focus $w"
-	bind $tl <Destroy> "
-		array unset browser_buffer $w_list
-		array unset browser_files $w_list
-		array unset browser_status $w_list
-		array unset browser_stack $w_list
-		array unset browser_path $w_list
-		array unset browser_commit $w_list
-		array unset browser_busy $w_list
-	"
-	wm title $tl "[appname] ([reponame]): File Browser"
-	ls_tree $w_list $browser_commit($w_list) {}
+	bind $w_list <Visibility> [list focus $w_list]
+	bind $w_list <Destroy> [list delete_this $this]
+	set w $w_list
+	_ls $this $browser_commit
+	return $this
 }
 
-proc browser_move {dir w} {
-	global browser_files browser_busy
-
-	if {$browser_busy($w)} return
+method _move {dir} {
+	if {$browser_busy} return
 	set lno [lindex [split [$w index in_sel.first] .] 0]
 	incr lno $dir
-	if {[lindex $browser_files($w) [expr {$lno - 1}]] ne {}} {
+	if {[lindex $browser_files [expr {$lno - 1}]] ne {}} {
 		$w tag remove in_sel 0.0 end
 		$w tag add in_sel $lno.0 [expr {$lno + 1}].0
 		$w see $lno.0
 	}
 }
 
-proc browser_page {dir w} {
-	global browser_files browser_busy
-
-	if {$browser_busy($w)} return
+method _page {dir} {
+	if {$browser_busy} return
 	$w yview scroll $dir pages
 	set lno [expr {int(
 		  [lindex [$w yview] 0]
-		* [llength $browser_files($w)]
+		* [llength $browser_files]
 		+ 1)}]
-	if {[lindex $browser_files($w) [expr {$lno - 1}]] ne {}} {
+	if {[lindex $browser_files [expr {$lno - 1}]] ne {}} {
 		$w tag remove in_sel 0.0 end
 		$w tag add in_sel $lno.0 [expr {$lno + 1}].0
 		$w see $lno.0
 	}
 }
 
-proc browser_parent {w} {
-	global browser_files browser_status browser_path
-	global browser_stack browser_busy
-
-	if {$browser_busy($w)} return
-	set info [lindex $browser_files($w) 0]
+method _parent {} {
+	if {$browser_busy} return
+	set info [lindex $browser_files 0]
 	if {[lindex $info 0] eq {parent}} {
-		set parent [lindex $browser_stack($w) end-1]
-		set browser_stack($w) [lrange $browser_stack($w) 0 end-2]
-		if {$browser_stack($w) eq {}} {
-			regsub {:.*$} $browser_path($w) {:} browser_path($w)
+		set parent [lindex $browser_stack end-1]
+		set browser_stack [lrange $browser_stack 0 end-2]
+		if {$browser_stack eq {}} {
+			regsub {:.*$} $browser_path {:} browser_path
 		} else {
-			regsub {/[^/]+$} $browser_path($w) {} browser_path($w)
+			regsub {/[^/]+$} $browser_path {} browser_path
 		}
-		set browser_status($w) "Loading $browser_path($w)..."
-		ls_tree $w [lindex $parent 0] [lindex $parent 1]
+		set browser_status "Loading $browser_path..."
+		_ls $this [lindex $parent 0] [lindex $parent 1]
 	}
 }
 
-proc browser_enter {w} {
-	global browser_files browser_status browser_path
-	global browser_commit browser_stack browser_busy
-
-	if {$browser_busy($w)} return
+method _enter {} {
+	if {$browser_busy} return
 	set lno [lindex [split [$w index in_sel.first] .] 0]
-	set info [lindex $browser_files($w) [expr {$lno - 1}]]
+	set info [lindex $browser_files [expr {$lno - 1}]]
 	if {$info ne {}} {
 		switch -- [lindex $info 0] {
 		parent {
-			browser_parent $w
+			_parent $this
 		}
 		tree {
 			set name [lindex $info 2]
 			set escn [escape_path $name]
-			set browser_status($w) "Loading $escn..."
-			append browser_path($w) $escn
-			ls_tree $w [lindex $info 1] $name
+			set browser_status "Loading $escn..."
+			append browser_path $escn
+			_ls $this [lindex $info 1] $name
 		}
 		blob {
 			set name [lindex $info 2]
 			set p {}
-			foreach n $browser_stack($w) {
+			foreach n $browser_stack {
 				append p [lindex $n 1]
 			}
 			append p $name
-			show_blame $browser_commit($w) $p
+			show_blame $browser_commit $p
 		}
 		}
 	}
 }
 
-proc browser_click {was_double_click w pos} {
-	global browser_files browser_busy
-
-	if {$browser_busy($w)} return
+method _click {was_double_click pos} {
+	if {$browser_busy} return
 	set lno [lindex [split [$w index $pos] .] 0]
 	focus $w
 
-	if {[lindex $browser_files($w) [expr {$lno - 1}]] ne {}} {
+	if {[lindex $browser_files [expr {$lno - 1}]] ne {}} {
 		$w tag remove in_sel 0.0 end
 		$w tag add in_sel $lno.0 [expr {$lno + 1}].0
 		if {$was_double_click} {
-			browser_enter $w
+			_enter $this
 		}
 	}
 }
 
-proc ls_tree {w tree_id name} {
-	global browser_buffer browser_files browser_stack browser_busy
-
-	set browser_buffer($w) {}
-	set browser_files($w) {}
-	set browser_busy($w) 1
+method _ls {tree_id {name {}}} {
+	set browser_buffer {}
+	set browser_files {}
+	set browser_busy 1
 
 	$w conf -state normal
 	$w tag remove in_sel 0.0 end
 	$w delete 0.0 end
-	if {$browser_stack($w) ne {}} {
+	if {$browser_stack ne {}} {
 		$w image create end \
 			-align center -padx 5 -pady 1 \
 			-name icon0 \
 			-image file_uplevel
 		$w insert end {[Up To Parent]}
-		lappend browser_files($w) parent
+		lappend browser_files parent
 	}
-	lappend browser_stack($w) [list $tree_id $name]
+	lappend browser_stack [list $tree_id $name]
 	$w conf -state disabled
 
 	set cmd [list git ls-tree -z $tree_id]
 	set fd [open "| $cmd" r]
 	fconfigure $fd -blocking 0 -translation binary -encoding binary
-	fileevent $fd readable [list read_ls_tree $fd $w]
+	fileevent $fd readable [cb _read $fd]
 }
 
-proc read_ls_tree {fd w} {
-	global browser_buffer browser_files browser_status browser_busy
+method _read {fd} {
+	append browser_buffer [read $fd]
+	set pck [split $browser_buffer "\0"]
+	set browser_buffer [lindex $pck end]
 
-	if {![winfo exists $w]} {
-		catch {close $fd}
-		return
-	}
-
-	append browser_buffer($w) [read $fd]
-	set pck [split $browser_buffer($w) "\0"]
-	set browser_buffer($w) [lindex $pck end]
-
-	set n [llength $browser_files($w)]
+	set n [llength $browser_files]
 	$w conf -state normal
 	foreach p [lrange $pck 0 end-1] {
 		set info [split $p "\t"]
@@ -246,18 +218,22 @@
 			-name icon[incr n] \
 			-image $image
 		$w insert end [escape_path $path]
-		lappend browser_files($w) [list $type $object $path]
+		lappend browser_files [list $type $object $path]
 	}
 	$w conf -state disabled
 
 	if {[eof $fd]} {
 		close $fd
-		set browser_status($w) Ready.
-		set browser_busy($w) 0
-		array unset browser_buffer $w
+		set browser_status Ready.
+		set browser_busy 0
+		unset browser_buffer
 		if {$n > 0} {
 			$w tag add in_sel 1.0 2.0
 			focus -force $w
 		}
 	}
+} ifdeleted {
+	catch {close $fd}
+}
+
 }
diff --git a/lib/console.tcl b/lib/console.tcl
index 75f3e04..8c112f3 100644
--- a/lib/console.tcl
+++ b/lib/console.tcl
@@ -1,30 +1,29 @@
 # git-gui console support
 # Copyright (C) 2006, 2007 Shawn Pearce
 
-namespace eval console {
+class console {
 
-variable next_console_id 0
-variable console_data
-variable console_cr
+field t_short
+field t_long
+field w
+field console_cr
 
-proc new {short_title long_title} {
-	variable next_console_id
-	variable console_data
-
-	set w .console[incr next_console_id]
-	set console_data($w) [list $short_title $long_title]
-	return [_init $w]
+constructor new {short_title long_title} {
+	set t_short $short_title
+	set t_long $long_title
+	_init $this
+	return $this
 }
 
-proc _init {w} {
+method _init {} {
 	global M1B
-	variable console_cr
-	variable console_data
+	make_toplevel top w
+	wm title $top "[appname] ([reponame]): $t_short"
+	set console_cr 1.0
 
-	set console_cr($w) 1.0
-	toplevel $w
 	frame $w.m
-	label $w.m.l1 -text "[lindex $console_data($w) 1]:" \
+	label $w.m.l1 \
+		-textvariable @t_long  \
 		-anchor w \
 		-justify left \
 		-font font_uibold
@@ -67,11 +66,9 @@
 	bind $w.m.t <$M1B-Key-a> "$w.m.t tag add sel 0.0 end;break"
 	bind $w.m.t <$M1B-Key-A> "$w.m.t tag add sel 0.0 end;break"
 	bind $w <Visibility> "focus $w"
-	wm title $w "[appname] ([reponame]): [lindex $console_data($w) 0]"
-	return $w
 }
 
-proc exec {w cmd {after {}}} {
+method exec {cmd {after {}}} {
 	# -- Cygwin's Tcl tosses the enviroment when we exec our child.
 	#    But most users need that so we have to relogin. :-(
 	#
@@ -86,16 +83,13 @@
 
 	set fd_f [open $cmd r]
 	fconfigure $fd_f -blocking 0 -translation binary
-	fileevent $fd_f readable \
-		[namespace code [list _read $w $fd_f $after]]
+	fileevent $fd_f readable [cb _read $fd_f $after]
 }
 
-proc _read {w fd after} {
-	variable console_cr
-
+method _read {fd after} {
 	set buf [read $fd]
 	if {$buf ne {}} {
-		if {![winfo exists $w]} {_init $w}
+		if {![winfo exists $w.m.t]} {_init $this}
 		$w.m.t conf -state normal
 		set c 0
 		set n [string length $buf]
@@ -107,11 +101,11 @@
 
 			if {$lf < $cr} {
 				$w.m.t insert end [string range $buf $c $lf]
-				set console_cr($w) [$w.m.t index {end -1c}]
+				set console_cr [$w.m.t index {end -1c}]
 				set c $lf
 				incr c
 			} else {
-				$w.m.t delete $console_cr($w) end
+				$w.m.t delete $console_cr end
 				$w.m.t insert end "\n"
 				$w.m.t insert end [string range $buf $c $cr]
 				set c $cr
@@ -130,19 +124,19 @@
 			set ok 1
 		}
 		if {$after ne {}} {
-			uplevel #0 $after $w $ok
+			uplevel #0 $after $ok
 		} else {
-			done $w $ok
+			done $this $ok
 		}
 		return
 	}
 	fconfigure $fd -blocking 0
 }
 
-proc chain {cmdlist w {ok 1}} {
+method chain {cmdlist {ok 1}} {
 	if {$ok} {
 		if {[llength $cmdlist] == 0} {
-			done $w $ok
+			done $this $ok
 			return
 		}
 
@@ -150,52 +144,33 @@
 		set cmdlist [lrange $cmdlist 1 end]
 
 		if {[lindex $cmd 0] eq {exec}} {
-			exec $w \
-				[lindex $cmd 1] \
-				[namespace code [list chain $cmdlist]]
+			exec $this \
+				[lrange $cmd 1 end] \
+				[cb chain $cmdlist]
 		} else {
-			uplevel #0 $cmd $cmdlist $w $ok
+			uplevel #0 $cmd [cb chain $cmdlist]
 		}
 	} else {
-		done $w $ok
+		done $this $ok
 	}
 }
 
-proc done {args} {
-	variable console_cr
-	variable console_data
-
-	switch -- [llength $args] {
-	2 {
-		set w [lindex $args 0]
-		set ok [lindex $args 1]
-	}
-	3 {
-		set w [lindex $args 1]
-		set ok [lindex $args 2]
-	}
-	default {
-		error "wrong number of args: done ?ignored? w ok"
-	}
-	}
-
+method done {ok} {
 	if {$ok} {
-		if {[winfo exists $w]} {
+		if {[winfo exists $w.m.s]} {
 			$w.m.s conf -background green -text {Success}
 			$w.ok conf -state normal
 			focus $w.ok
 		}
 	} else {
-		if {![winfo exists $w]} {
-			_init $w
+		if {![winfo exists $w.m.s]} {
+			_init $this
 		}
 		$w.m.s conf -background red -text {Error: Command Failed}
 		$w.ok conf -state normal
 		focus $w.ok
 	}
-
-	array unset console_cr $w
-	array unset console_data $w
+	delete_this
 }
 
 }
diff --git a/lib/database.tcl b/lib/database.tcl
index 73058a8..43e4a28 100644
--- a/lib/database.tcl
+++ b/lib/database.tcl
@@ -70,12 +70,12 @@
 
 proc do_gc {} {
 	set w [console::new {gc} {Compressing the object database}]
-	console::chain {
-		{exec {git pack-refs --prune}}
-		{exec {git reflog expire --all}}
-		{exec {git repack -a -d -l}}
-		{exec {git rerere gc}}
-	} $w
+	console::chain $w {
+		{exec git pack-refs --prune}
+		{exec git reflog expire --all}
+		{exec git repack -a -d -l}
+		{exec git rerere gc}
+	}
 }
 
 proc do_fsck_objects {} {
diff --git a/lib/merge.tcl b/lib/merge.tcl
index 642d6ee..24ed24b 100644
--- a/lib/merge.tcl
+++ b/lib/merge.tcl
@@ -123,7 +123,8 @@
 	set msg "Merging $current_branch, [join $names {, }]"
 	set ui_status_value "$msg..."
 	set cons [console::new "Merge" $msg]
-	console::exec $cons $cmd [namespace code [list _finish $revcnt]]
+	console::exec $cons $cmd \
+		[namespace code [list _finish $revcnt $cons]]
 	bind $w <Destroy> {}
 	destroy $w
 }