]>
Commit | Line | Data |
---|---|---|
b29bd5ca SP |
1 | # git-gui status bar mega-widget |
2 | # Copyright (C) 2007 Shawn Pearce | |
3 | ||
d9c6469f JG |
4 | # The status_bar class manages the entire status bar. It is possible for |
5 | # multiple overlapping asynchronous operations to want to display status | |
6 | # simultaneously. Each one receives a status_bar_operation when it calls the | |
7 | # start method, and the status bar combines all active operations into the | |
8 | # line of text it displays. Most of the time, there will be at most one | |
9 | # ongoing operation. | |
10 | # | |
11 | # Note that the entire status bar can be either in single-line or two-line | |
12 | # mode, depending on the constructor. Multiple active operations are only | |
13 | # supported for single-line status bars. | |
14 | ||
b29bd5ca SP |
15 | class status_bar { |
16 | ||
d9c6469f JG |
17 | field allow_multiple ; # configured at construction |
18 | ||
b29bd5ca SP |
19 | field w ; # our own window path |
20 | field w_l ; # text widget we draw messages into | |
21 | field w_c ; # canvas we draw a progress bar into | |
96225dbe | 22 | field c_pack ; # script to pack the canvas with |
d9c6469f JG |
23 | |
24 | field baseline_text ; # text to show if there are no operations | |
25 | field status_bar_text ; # combined text for all operations | |
26 | ||
27 | field operations ; # list of current ongoing operations | |
28 | ||
29 | # The status bar can display a progress bar, updated when consumers call the | |
30 | # update method on their status_bar_operation. When there are multiple | |
31 | # operations, the status bar shows the combined status of all operations. | |
32 | # | |
33 | # When an overlapping operation completes, the progress bar is going to | |
34 | # abruptly have one fewer operation in the calculation, causing a discontinuity. | |
35 | # Therefore, whenever an operation completes, if it is not the last operation, | |
36 | # this counter is increased, and the progress bar is calculated as though there | |
37 | # were still another operation at 100%. When the last operation completes, this | |
38 | # is reset to 0. | |
39 | field completed_operation_count | |
b29bd5ca SP |
40 | |
41 | constructor new {path} { | |
c80d7be5 | 42 | global use_ttk NS |
b29bd5ca SP |
43 | set w $path |
44 | set w_l $w.l | |
45 | set w_c $w.c | |
46 | ||
d9c6469f JG |
47 | # Standard single-line status bar: Permit overlapping operations |
48 | set allow_multiple 1 | |
49 | ||
50 | set baseline_text "" | |
51 | set operations [list] | |
52 | set completed_operation_count 0 | |
53 | ||
c80d7be5 PT |
54 | ${NS}::frame $w |
55 | if {!$use_ttk} { | |
56 | $w configure -borderwidth 1 -relief sunken | |
57 | } | |
58 | ${NS}::label $w_l \ | |
d9c6469f | 59 | -textvariable @status_bar_text \ |
b29bd5ca SP |
60 | -anchor w \ |
61 | -justify left | |
62 | pack $w_l -side left | |
96225dbe SP |
63 | set c_pack [cb _oneline_pack] |
64 | ||
65 | bind $w <Destroy> [cb _delete %W] | |
66 | return $this | |
67 | } | |
68 | ||
69 | method _oneline_pack {} { | |
70 | $w_c conf -width 100 | |
71 | pack $w_c -side right | |
72 | } | |
73 | ||
74 | constructor two_line {path} { | |
b963d118 | 75 | global NS |
96225dbe SP |
76 | set w $path |
77 | set w_l $w.l | |
78 | set w_c $w.c | |
79 | ||
d9c6469f JG |
80 | # Two-line status bar: Only one ongoing operation permitted. |
81 | set allow_multiple 0 | |
82 | ||
83 | set baseline_text "" | |
84 | set operations [list] | |
85 | set completed_operation_count 0 | |
86 | ||
c80d7be5 PT |
87 | ${NS}::frame $w |
88 | ${NS}::label $w_l \ | |
d9c6469f | 89 | -textvariable @status_bar_text \ |
96225dbe SP |
90 | -anchor w \ |
91 | -justify left | |
92 | pack $w_l -anchor w -fill x | |
93 | set c_pack [list pack $w_c -fill x] | |
b29bd5ca SP |
94 | |
95 | bind $w <Destroy> [cb _delete %W] | |
96 | return $this | |
97 | } | |
98 | ||
d9c6469f | 99 | method ensure_canvas {} { |
b29bd5ca SP |
100 | if {[winfo exists $w_c]} { |
101 | $w_c coords bar 0 0 0 20 | |
102 | } else { | |
103 | canvas $w_c \ | |
b29bd5ca SP |
104 | -height [expr {int([winfo reqheight $w_l] * 0.6)}] \ |
105 | -borderwidth 1 \ | |
106 | -relief groove \ | |
107 | -highlightt 0 | |
108 | $w_c create rectangle 0 0 0 20 -tags bar -fill navy | |
96225dbe | 109 | eval $c_pack |
b29bd5ca | 110 | } |
d9c6469f JG |
111 | } |
112 | ||
113 | method show {msg} { | |
114 | $this ensure_canvas | |
115 | set baseline_text $msg | |
116 | $this refresh | |
117 | } | |
118 | ||
119 | method start {msg {uds {}}} { | |
120 | set baseline_text "" | |
121 | ||
122 | if {!$allow_multiple && [llength $operations]} { | |
123 | return [lindex $operations 0] | |
124 | } | |
125 | ||
126 | $this ensure_canvas | |
127 | ||
128 | set operation [status_bar_operation::new $this $msg $uds] | |
129 | ||
130 | lappend operations $operation | |
131 | ||
132 | $this refresh | |
133 | ||
134 | return $operation | |
135 | } | |
136 | ||
137 | method refresh {} { | |
138 | set new_text "" | |
139 | ||
140 | set total [expr $completed_operation_count * 100] | |
141 | set have $total | |
142 | ||
143 | foreach operation $operations { | |
144 | if {$new_text != ""} { | |
145 | append new_text " / " | |
146 | } | |
147 | ||
148 | append new_text [$operation get_status] | |
149 | ||
150 | set total [expr $total + 100] | |
151 | set have [expr $have + [$operation get_progress]] | |
152 | } | |
153 | ||
154 | if {$new_text == ""} { | |
155 | set new_text $baseline_text | |
156 | } | |
157 | ||
158 | set status_bar_text $new_text | |
159 | ||
160 | if {[winfo exists $w_c]} { | |
161 | set pixel_width 0 | |
162 | if {$have > 0} { | |
163 | set pixel_width [expr {[winfo width $w_c] * $have / $total}] | |
164 | } | |
165 | ||
166 | $w_c coords bar 0 0 $pixel_width 20 | |
167 | } | |
168 | } | |
169 | ||
170 | method stop {operation stop_msg} { | |
171 | set idx [lsearch $operations $operation] | |
172 | ||
173 | if {$idx >= 0} { | |
174 | set operations [lreplace $operations $idx $idx] | |
175 | set completed_operation_count [expr \ | |
176 | $completed_operation_count + 1] | |
177 | ||
178 | if {[llength $operations] == 0} { | |
179 | set completed_operation_count 0 | |
180 | ||
181 | destroy $w_c | |
182 | if {$stop_msg ne {}} { | |
183 | set baseline_text $stop_msg | |
184 | } | |
185 | } | |
186 | ||
187 | $this refresh | |
188 | } | |
189 | } | |
190 | ||
191 | method stop_all {{stop_msg {}}} { | |
192 | # This makes the operation's call to stop a no-op. | |
193 | set operations_copy $operations | |
194 | set operations [list] | |
195 | ||
196 | foreach operation $operations_copy { | |
197 | $operation stop | |
198 | } | |
199 | ||
200 | if {$stop_msg ne {}} { | |
201 | set baseline_text $stop_msg | |
202 | } | |
203 | ||
204 | $this refresh | |
205 | } | |
206 | ||
207 | method _delete {current} { | |
208 | if {$current eq $w} { | |
209 | delete_this | |
210 | } | |
211 | } | |
212 | ||
213 | } | |
214 | ||
215 | # The status_bar_operation class tracks a single consumer's ongoing status bar | |
216 | # activity, with the context that there are a few situations where multiple | |
217 | # overlapping asynchronous operations might want to display status information | |
218 | # simultaneously. Instances of status_bar_operation are created by calling | |
219 | # start on the status_bar, and when the caller is done with its stauts bar | |
220 | # operation, it calls stop on the operation. | |
221 | ||
222 | class status_bar_operation { | |
223 | ||
224 | field status_bar; # reference back to the status_bar that owns this object | |
225 | ||
226 | field is_active; | |
227 | ||
228 | field status {}; # single line of text we show | |
229 | field progress {}; # current progress (0 to 100) | |
230 | field prefix {}; # text we format into status | |
231 | field units {}; # unit of progress | |
232 | field meter {}; # current core git progress meter (if active) | |
233 | ||
234 | constructor new {owner msg uds} { | |
235 | set status_bar $owner | |
b29bd5ca SP |
236 | |
237 | set status $msg | |
d9c6469f | 238 | set progress 0 |
b29bd5ca SP |
239 | set prefix $msg |
240 | set units $uds | |
b7922306 | 241 | set meter {} |
d9c6469f JG |
242 | |
243 | set is_active 1 | |
244 | ||
245 | return $this | |
b29bd5ca SP |
246 | } |
247 | ||
d9c6469f JG |
248 | method get_is_active {} { return $is_active } |
249 | method get_status {} { return $status } | |
250 | method get_progress {} { return $progress } | |
251 | ||
b29bd5ca | 252 | method update {have total} { |
d9c6469f JG |
253 | if {!$is_active} { return } |
254 | ||
255 | set progress 0 | |
256 | ||
b29bd5ca | 257 | if {$total > 0} { |
d9c6469f | 258 | set progress [expr {100 * $have / $total}] |
b29bd5ca SP |
259 | } |
260 | ||
96225dbe | 261 | set prec [string length [format %i $total]] |
d9c6469f | 262 | |
96225dbe SP |
263 | set status [mc "%s ... %*i of %*i %s (%3i%%)" \ |
264 | $prefix \ | |
265 | $prec $have \ | |
266 | $prec $total \ | |
d9c6469f JG |
267 | $units $progress] |
268 | ||
269 | $status_bar refresh | |
b29bd5ca SP |
270 | } |
271 | ||
b7922306 | 272 | method update_meter {buf} { |
d9c6469f JG |
273 | if {!$is_active} { return } |
274 | ||
b7922306 SP |
275 | append meter $buf |
276 | set r [string last "\r" $meter] | |
277 | if {$r == -1} { | |
278 | return | |
279 | } | |
280 | ||
281 | set prior [string range $meter 0 $r] | |
282 | set meter [string range $meter [expr {$r + 1}] end] | |
bea6878b SP |
283 | set p "\\((\\d+)/(\\d+)\\)" |
284 | if {[regexp ":\\s*\\d+% $p\(?:, done.\\s*\n|\\s*\r)\$" $prior _j a b]} { | |
285 | update $this $a $b | |
286 | } elseif {[regexp "$p\\s+done\r\$" $prior _j a b]} { | |
b7922306 SP |
287 | update $this $a $b |
288 | } | |
289 | } | |
290 | ||
d9c6469f JG |
291 | method stop {{stop_msg {}}} { |
292 | if {$is_active} { | |
293 | set is_active 0 | |
294 | $status_bar stop $this $stop_msg | |
b7922306 | 295 | } |
b29bd5ca SP |
296 | } |
297 | ||
d9c6469f JG |
298 | method restart {msg} { |
299 | if {!$is_active} { return } | |
300 | ||
301 | set status $msg | |
302 | set prefix $msg | |
303 | set meter {} | |
304 | $status_bar refresh | |
b29bd5ca SP |
305 | } |
306 | ||
d9c6469f JG |
307 | method _delete {} { |
308 | stop | |
309 | delete_this | |
b29bd5ca SP |
310 | } |
311 | ||
312 | } |