1# git-gui revision chooser
2# Copyright (C) 2006, 2007 Shawn Pearce
3
4class choose_rev {
5
6image create photo ::choose_rev::img_find -data {R0lGODlhEAAQAIYAAPwCBCQmJDw+PBQSFAQCBMza3NTm5MTW1HyChOT29Ozq7MTq7Kze5Kzm7Oz6/NTy9Iza5GzGzKzS1Nzy9Nz29Kzq9HTGzHTK1Lza3AwKDLzu9JTi7HTW5GTCzITO1Mzq7Hza5FTK1ESyvHzKzKzW3DQyNDyqtDw6PIzW5HzGzAT+/Dw+RKyurNTOzMTGxMS+tJSGdATCxHRydLSqpLymnLSijBweHERCRNze3Pz69PTy9Oze1OTSxOTGrMSqlLy+vPTu5OzSvMymjNTGvNS+tMy2pMyunMSefAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAe4gACCAAECA4OIiAIEBQYHBAKJgwIICQoLDA0IkZIECQ4PCxARCwSSAxITFA8VEBYXGBmJAQYLGhUbHB0eH7KIGRIMEBAgISIjJKaIJQQLFxERIialkieUGigpKRoIBCqJKyyLBwvJAioEyoICLS4v6QQwMQQyLuqLli8zNDU2BCf1lN3AkUPHDh49fAQAAEnGD1MCCALZEaSHkIUMBQS8wWMIkSJGhBzBmFEGgRsBUqpMiSgdAD+BAAAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7}
7
8field w ; # our megawidget path
9field w_list ; # list of currently filtered specs
10field w_filter ; # filter entry for $w_list
11
12field c_expr {}; # current revision expression
13field filter ; # current filter string
14field revtype head; # type of revision chosen
15field cur_specs [list]; # list of specs for $revtype
16field spec_head ; # list of all head specs
17field spec_trck ; # list of all tracking branch specs
18field spec_tag ; # list of all tag specs
19
20constructor new {path {title {}}} {
21 global current_branch is_detached
22
23 set w $path
24
25 if {$title ne {}} {
26 labelframe $w -text $title
27 } else {
28 frame $w
29 }
30 bind $w <Destroy> [cb _delete %W]
31
32 if {$is_detached} {
33 radiobutton $w.detachedhead_r \
34 -anchor w \
35 -text {This Detached Checkout} \
36 -value HEAD \
37 -variable @revtype
38 grid $w.detachedhead_r -sticky we -padx {0 5} -columnspan 2
39 }
40
41 radiobutton $w.expr_r \
42 -text {Revision Expression:} \
43 -value expr \
44 -variable @revtype
45 entry $w.expr_t \
46 -borderwidth 1 \
47 -relief sunken \
48 -width 50 \
49 -textvariable @c_expr \
50 -validate key \
51 -validatecommand [cb _validate %d %S]
52 grid $w.expr_r $w.expr_t -sticky we -padx {0 5}
53
54 frame $w.types
55 radiobutton $w.types.head_r \
56 -text {Local Branch} \
57 -value head \
58 -variable @revtype
59 pack $w.types.head_r -side left
60 radiobutton $w.types.trck_r \
61 -text {Tracking Branch} \
62 -value trck \
63 -variable @revtype
64 pack $w.types.trck_r -side left
65 radiobutton $w.types.tag_r \
66 -text {Tag} \
67 -value tag \
68 -variable @revtype
69 pack $w.types.tag_r -side left
70 set w_filter $w.types.filter
71 entry $w_filter \
72 -borderwidth 1 \
73 -relief sunken \
74 -width 12 \
75 -textvariable @filter \
76 -validate key \
77 -validatecommand [cb _filter %P]
78 pack $w_filter -side right
79 pack [label $w.types.filter_icon \
80 -image ::choose_rev::img_find \
81 ] -side right
82 grid $w.types -sticky we -padx {0 5} -columnspan 2
83
84 frame $w.list
85 set w_list $w.list.l
86 listbox $w_list \
87 -font font_diff \
88 -width 50 \
89 -height 5 \
90 -selectmode browse \
91 -exportselection false \
92 -xscrollcommand [cb _sb_set $w.list.sbx h] \
93 -yscrollcommand [cb _sb_set $w.list.sby v]
94 pack $w_list -fill both -expand 1
95 grid $w.list -sticky nswe -padx {20 5} -columnspan 2
96
97 grid columnconfigure $w 1 -weight 1
98 if {$is_detached} {
99 grid rowconfigure $w 3 -weight 1
100 } else {
101 grid rowconfigure $w 2 -weight 1
102 }
103
104 trace add variable @revtype write [cb _select]
105 bind $w_filter <Key-Return> [list focus $w_list]\;break
106 bind $w_filter <Key-Down> [list focus $w_list]
107
108 set spec_head [list]
109 foreach name [load_all_heads] {
110 lappend spec_head [list $name refs/heads/$name]
111 }
112
113 set spec_trck [list]
114 foreach spec [all_tracking_branches] {
115 set name [lindex $spec 0]
116 regsub ^refs/(heads|remotes)/ $name {} name
117 lappend spec_trck [concat $name $spec]
118 }
119
120 set spec_tag [list]
121 foreach name [load_all_tags] {
122 lappend spec_tag [list $name refs/tags/$name]
123 }
124
125 if {$is_detached} { set revtype HEAD
126 } elseif {[llength $spec_head] > 0} { set revtype head
127 } elseif {[llength $spec_trck] > 0} { set revtype trck
128 } elseif {[llength $spec_tag ] > 0} { set revtype tag
129 } else { set revtype expr
130 }
131
132 if {$revtype eq {head} && $current_branch ne {}} {
133 set i 0
134 foreach spec $spec_head {
135 if {[lindex $spec 0] eq $current_branch} {
136 $w_list selection clear 0 end
137 $w_list selection set $i
138 break
139 }
140 incr i
141 }
142 }
143
144 return $this
145}
146
147method none {text} {
148 if {![winfo exists $w.none_r]} {
149 radiobutton $w.none_r \
150 -anchor w \
151 -value none \
152 -variable @revtype
153 grid $w.none_r -sticky we -padx {0 5} -columnspan 2
154 }
155 $w.none_r configure -text $text
156}
157
158method get {} {
159 switch -- $revtype {
160 head -
161 trck -
162 tag {
163 set i [$w_list curselection]
164 if {$i ne {}} {
165 return [lindex $cur_specs $i 0]
166 } else {
167 return {}
168 }
169 }
170
171 HEAD { return HEAD }
172 expr { return $c_expr }
173 none { return {} }
174 default { error "unknown type of revision" }
175 }
176}
177
178method pick_tracking_branch {} {
179 set revtype trck
180}
181
182method focus_filter {} {
183 if {[$w_filter cget -state] eq {normal}} {
184 focus $w_filter
185 }
186}
187
188method bind_listbox {event script} {
189 bind $w_list $event $script
190}
191
192method get_local_branch {} {
193 if {$revtype eq {head}} {
194 return [_expr $this]
195 } else {
196 return {}
197 }
198}
199
200method get_tracking_branch {} {
201 set i [$w_list curselection]
202 if {$i eq {} || $revtype ne {trck}} {
203 return {}
204 }
205 return [lrange [lindex $cur_specs $i] 1 end]
206}
207
208method get_commit {} {
209 set e [_expr $this]
210 if {$e eq {}} {
211 return {}
212 }
213 return [git rev-parse --verify "$e^0"]
214}
215
216method commit_or_die {} {
217 if {[catch {set new [get_commit $this]} err]} {
218
219 # Cleanup the not-so-friendly error from rev-parse.
220 #
221 regsub {^fatal:\s*} $err {} err
222 if {$err eq {Needed a single revision}} {
223 set err {}
224 }
225
226 set top [winfo toplevel $w]
227 set msg "Invalid revision: [get $this]\n\n$err"
228 tk_messageBox \
229 -icon error \
230 -type ok \
231 -title [wm title $top] \
232 -parent $top \
233 -message $msg
234 error $msg
235 }
236 return $new
237}
238
239method _expr {} {
240 switch -- $revtype {
241 head -
242 trck -
243 tag {
244 set i [$w_list curselection]
245 if {$i ne {}} {
246 return [lindex $cur_specs $i 1]
247 } else {
248 error "No revision selected."
249 }
250 }
251
252 expr {
253 if {$c_expr ne {}} {
254 return $c_expr
255 } else {
256 error "Revision expression is empty."
257 }
258 }
259 HEAD { return HEAD }
260 none { return {} }
261 default { error "unknown type of revision" }
262 }
263}
264
265method _validate {d S} {
266 if {$d == 1} {
267 if {[regexp {\s} $S]} {
268 return 0
269 }
270 if {[string length $S] > 0} {
271 set revtype expr
272 }
273 }
274 return 1
275}
276
277method _filter {P} {
278 if {[regexp {\s} $P]} {
279 return 0
280 }
281 _rebuild $this $P
282 return 1
283}
284
285method _select {args} {
286 _rebuild $this $filter
287 focus_filter $this
288}
289
290method _rebuild {pat} {
291 set ste normal
292 switch -- $revtype {
293 head { set new $spec_head }
294 trck { set new $spec_trck }
295 tag { set new $spec_tag }
296 expr -
297 HEAD -
298 none {
299 set new [list]
300 set ste disabled
301 }
302 }
303
304 if {[$w_list cget -state] eq {disabled}} {
305 $w_list configure -state normal
306 }
307 $w_list delete 0 end
308
309 if {$pat ne {}} {
310 set pat *${pat}*
311 }
312 set cur_specs [list]
313 foreach spec $new {
314 set txt [lindex $spec 0]
315 if {$pat eq {} || [string match $pat $txt]} {
316 lappend cur_specs $spec
317 $w_list insert end $txt
318 }
319 }
320 if {$cur_specs ne {}} {
321 $w_list selection clear 0 end
322 $w_list selection set 0
323 }
324
325 if {[$w_filter cget -state] ne $ste} {
326 $w_list configure -state $ste
327 $w_filter configure -state $ste
328 }
329}
330
331method _delete {current} {
332 if {$current eq $w} {
333 delete_this
334 }
335}
336
337method _sb_set {sb orient first last} {
338 set old_focus [focus -lastfor $w]
339
340 if {$first == 0 && $last == 1} {
341 if {[winfo exists $sb]} {
342 destroy $sb
343 if {$old_focus ne {}} {
344 update
345 focus $old_focus
346 }
347 }
348 return
349 }
350
351 if {![winfo exists $sb]} {
352 if {$orient eq {h}} {
353 scrollbar $sb -orient h -command [list $w_list xview]
354 pack $sb -fill x -side bottom -before $w_list
355 } else {
356 scrollbar $sb -orient v -command [list $w_list yview]
357 pack $sb -fill y -side right -before $w_list
358 }
359 if {$old_focus ne {}} {
360 update
361 focus $old_focus
362 }
363 }
364 $sb set $first $last
365}
366
367}