Subject: Re: [PATCH] git-gui: offer a list of recent repositories on startup

From: "Shawn O. Pearce" <[email-masked]>
Date: Wed, 10 Oct 2007 03:30:53 -0400

Steffen Prohaska <[email-masked]> wrote:
> Shawn,
> I attached two patches. They should eventually be both squashed into  
> one.

Thanks.  I'm applying the patch below over both of yours, and
then squashing them all together and keeping you as the author.

I did not like how the listbox looked (or worked!) on Mac OS X.
Clicking on the item was at first a very non-obvious action, and
then when I did do it I was very shocked to see the repository open
immediately.  My brain knew the action was wired to the selection
event (I obviously read the two patches before applying them),
but it didn't expect that behavior.  Clearly the wrong UI model.

The patch below uses a text widget instead and renders each of
the repositories as a blue underlined link.  Users will generally
assume that clicking on such links will take them immediately to
that repository (much as it would take them to another webpage if
this was a web browser).

The "Open Recent Repositories" is not even shown in the UI if there
aren't any to offer to the user.  Showing them this area just looks
silly if they haven't opened or cloned anything yet.

We now add freshly created or cloned repositories to the list of
recently opened repositories.  These are just as fair game for
being recently accessed as any others.  Perhaps even more so as
users may wonder where they cloned that repository to the last time
they started git-gui.

We always store the absolute path of a repository into the config.
This avoids the mess of opening a repository twice using say a
relative path entered into the text entry field and then opening
it later via a file browser, only to find out you now have the same
repository in the recent list twice.

I fixed the --unset-all bug and also avoided the --unset-all/--add
loop you were using.  We convert the value into a "safe" expression
and then try to match on it.

---

diff --git a/lib/choose_repository.tcl b/lib/choose_repository.tcl
index 4deb1ec..bdfdbae 100644
--- a/lib/choose_repository.tcl
+++ b/lib/choose_repository.tcl
@@ -80,27 +80,35 @@ constructor pick {} {
 		-text [mc "Open Existing Repository"] \
 		-variable @action \
 		-value open
-	label $w_body.space
-	label $w_body.recentlabel \
-		-anchor w \
-		-text [mc "Select Recent Repository:"]
-	set w_recentlist $w_body.recentlist
-	listbox $w_recentlist \
-		-relief flat \
-		-width 50 \
-		-height 10 \
-		-exportselection false \
-		-selectmode select
-	foreach p [_get_recentrepos] {
-		$w_recentlist insert end $p
-	}
-	bind $w_recentlist <<ListboxSelect>> [cb _open_recent]
 	pack $w_body.new -anchor w -fill x
 	pack $w_body.clone -anchor w -fill x
 	pack $w_body.open -anchor w -fill x
-	pack $w_body.space -anchor w -fill x
-	pack $w_body.recentlabel -anchor w -fill x
-	pack $w_recentlist -anchor w -fill x
+
+	set recent [_get_recentrepos]
+	if {[llength $recent] > 0} {
+		label $w_body.space
+		label $w_body.recentlabel \
+			-anchor w \
+			-text [mc "Open Recent Repository:"]
+		set w_recentlist $w_body.recentlist
+		text $w_recentlist \
+			-cursor $::cursor_ptr \
+			-wrap none \
+			-width 50 \
+			-height 10
+		$w_recentlist tag conf link \
+			-foreground blue \
+			-underline 1
+		foreach p $recent {
+			$w_recentlist insert end $p link
+			$w_recentlist insert end "\n"
+		}
+		$w_recentlist conf -state disabled
+		$w_recentlist tag bind link <1> [cb _open_recent %x,%y]
+		pack $w_body.space -anchor w -fill x
+		pack $w_body.recentlabel -anchor w -fill x
+		pack $w_recentlist -anchor w -fill x
+	}
 	pack $w_body -fill x -padx 10 -pady 10
 
 	frame $w.buttons
@@ -164,23 +172,26 @@ proc _get_recentrepos {} {
 }
 
 proc _append_recentrepos {path} {
+	set path [file normalize $path]
 	set recent [get_config gui.recentrepo]
 	if {[lsearch $recent $path] >= 0} {
 		return
 	}
+
 	lappend recent $path
-	if {[llength $recent] > 10} {
+	git config --global --add gui.recentrepo $path
+
+	while {[llength $recent] > 10} {
+		set p [lindex $recent 0]
 		set recent [lrange $recent 1 end]
-	}
-	git config --global --unset-all gui.recentrepo
-	foreach p $recent {
-		git config --global --add gui.recentrepo $p
+		regsub -all -- {([()\[\]{}\.^$+*?\\])} $p {\\\1} p
+		git config --global --unset gui.recentrepo "^$p\$"
 	}
 }
 
-method _open_recent {} {
-	set id [$w_recentlist curselection]
-	set local_path [$w_recentlist get $id]
+method _open_recent {xy} {
+	set id [lindex [split [$w_recentlist index @$xy] .] 0]
+	set local_path [$w_recentlist get $id.0 $id.end]
 	_do_open2 $this
 }
 
@@ -224,6 +235,7 @@ method _git_init {} {
 		return 0
 	}
 
+	_append_recentrepos [pwd]
 	set ::_gitdir .git
 	set ::_prefix {}
 	return 1
@@ -906,8 +918,7 @@ method _do_open2 {} {
 		return
 	}
 
-	_append_recentrepos $local_path
-
+	_append_recentrepos [pwd]
 	set ::_gitdir .git
 	set ::_prefix {}
 	set done 1

-- 
Shawn.