| 1 | # git-gui revision chooser |
| 2 | # Copyright (C) 2006, 2007 Shawn Pearce |
| 3 | |
| 4 | class choose_rev { |
| 5 | |
| 6 | image create photo ::choose_rev::img_find -data {R0lGODlhEAAQAIYAAPwCBCQmJDw+PBQSFAQCBMza3NTm5MTW1HyChOT29Ozq7MTq7Kze5Kzm7Oz6/NTy9Iza5GzGzKzS1Nzy9Nz29Kzq9HTGzHTK1Lza3AwKDLzu9JTi7HTW5GTCzITO1Mzq7Hza5FTK1ESyvHzKzKzW3DQyNDyqtDw6PIzW5HzGzAT+/Dw+RKyurNTOzMTGxMS+tJSGdATCxHRydLSqpLymnLSijBweHERCRNze3Pz69PTy9Oze1OTSxOTGrMSqlLy+vPTu5OzSvMymjNTGvNS+tMy2pMyunMSefAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAe4gACCAAECA4OIiAIEBQYHBAKJgwIICQoLDA0IkZIECQ4PCxARCwSSAxITFA8VEBYXGBmJAQYLGhUbHB0eH7KIGRIMEBAgISIjJKaIJQQLFxERIialkieUGigpKRoIBCqJKyyLBwvJAioEyoICLS4v6QQwMQQyLuqLli8zNDU2BCf1lN3AkUPHDh49fAQAAEnGD1MCCALZEaSHkIUMBQS8wWMIkSJGhBzBmFEGgRsBUqpMiSgdAD+BAAAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7} |
| 7 | |
| 8 | field w ; # our megawidget path |
| 9 | field w_list ; # list of currently filtered specs |
| 10 | field w_filter ; # filter entry for $w_list |
| 11 | |
| 12 | field c_expr {}; # current revision expression |
| 13 | field filter ""; # current filter string |
| 14 | field revtype head; # type of revision chosen |
| 15 | field cur_specs [list]; # list of specs for $revtype |
| 16 | field spec_head ; # list of all head specs |
| 17 | field spec_trck ; # list of all tracking branch specs |
| 18 | field spec_tag ; # list of all tag specs |
| 19 | field tip_data ; # array of tip commit info by refname |
| 20 | field log_last ; # array of reflog date by refname |
| 21 | |
| 22 | field tooltip_wm {} ; # Current tooltip toplevel, if open |
| 23 | field tooltip_t {} ; # Text widget in $tooltip_wm |
| 24 | field tooltip_timer {} ; # Current timer event for our tooltip |
| 25 | |
| 26 | proc new {path {title {}}} { |
| 27 | return [_new $path 0 $title] |
| 28 | } |
| 29 | |
| 30 | proc new_unmerged {path {title {}}} { |
| 31 | return [_new $path 1 $title] |
| 32 | } |
| 33 | |
| 34 | constructor _new {path unmerged_only title} { |
| 35 | global current_branch is_detached |
| 36 | |
| 37 | if {![info exists ::all_remotes]} { |
| 38 | load_all_remotes |
| 39 | } |
| 40 | |
| 41 | set w $path |
| 42 | |
| 43 | if {$title ne {}} { |
| 44 | ttk::labelframe $w -text $title |
| 45 | } else { |
| 46 | ttk::frame $w |
| 47 | } |
| 48 | bind $w <Destroy> [cb _delete %W] |
| 49 | |
| 50 | if {$is_detached} { |
| 51 | ttk::radiobutton $w.detachedhead_r \ |
| 52 | -text [mc "This Detached Checkout"] \ |
| 53 | -value HEAD \ |
| 54 | -variable @revtype |
| 55 | grid $w.detachedhead_r -sticky we -padx {0 5} -columnspan 2 |
| 56 | } |
| 57 | |
| 58 | ttk::radiobutton $w.expr_r \ |
| 59 | -text [mc "Revision Expression:"] \ |
| 60 | -value expr \ |
| 61 | -variable @revtype |
| 62 | ttk::entry $w.expr_t \ |
| 63 | -width 50 \ |
| 64 | -textvariable @c_expr \ |
| 65 | -validate key \ |
| 66 | -validatecommand [cb _validate %d %S] |
| 67 | grid $w.expr_r $w.expr_t -sticky we -padx {0 5} |
| 68 | |
| 69 | ttk::frame $w.types |
| 70 | ttk::radiobutton $w.types.head_r \ |
| 71 | -text [mc "Local Branch"] \ |
| 72 | -value head \ |
| 73 | -variable @revtype |
| 74 | pack $w.types.head_r -side left |
| 75 | ttk::radiobutton $w.types.trck_r \ |
| 76 | -text [mc "Tracking Branch"] \ |
| 77 | -value trck \ |
| 78 | -variable @revtype |
| 79 | pack $w.types.trck_r -side left |
| 80 | ttk::radiobutton $w.types.tag_r \ |
| 81 | -text [mc "Tag"] \ |
| 82 | -value tag \ |
| 83 | -variable @revtype |
| 84 | pack $w.types.tag_r -side left |
| 85 | set w_filter $w.types.filter |
| 86 | ttk::entry $w_filter \ |
| 87 | -width 12 \ |
| 88 | -textvariable @filter \ |
| 89 | -validate key \ |
| 90 | -validatecommand [cb _filter %P] |
| 91 | pack $w_filter -side right |
| 92 | pack [ttk::label $w.types.filter_icon \ |
| 93 | -image ::choose_rev::img_find \ |
| 94 | ] -side right |
| 95 | grid $w.types -sticky we -padx {0 5} -columnspan 2 |
| 96 | |
| 97 | ttk::frame $w.list -style SListbox.TFrame -padding 2 |
| 98 | set w_list $w.list.l |
| 99 | listbox $w_list \ |
| 100 | -font font_diff \ |
| 101 | -width 50 \ |
| 102 | -height 10 \ |
| 103 | -selectmode browse \ |
| 104 | -exportselection false \ |
| 105 | -xscrollcommand [cb _sb_set $w.list.sbx h] \ |
| 106 | -yscrollcommand [cb _sb_set $w.list.sby v] |
| 107 | $w_list configure -relief flat -highlightthickness 0 -borderwidth 0 |
| 108 | pack $w_list -fill both -expand 1 |
| 109 | grid $w.list -sticky nswe -padx {20 5} -columnspan 2 |
| 110 | bind $w_list <Any-Motion> [cb _show_tooltip @%x,%y] |
| 111 | bind $w_list <Any-Enter> [cb _hide_tooltip] |
| 112 | bind $w_list <Any-Leave> [cb _hide_tooltip] |
| 113 | bind $w_list <Destroy> [cb _hide_tooltip] |
| 114 | |
| 115 | grid columnconfigure $w 1 -weight 1 |
| 116 | if {$is_detached} { |
| 117 | grid rowconfigure $w 3 -weight 1 |
| 118 | } else { |
| 119 | grid rowconfigure $w 2 -weight 1 |
| 120 | } |
| 121 | |
| 122 | trace add variable @revtype write [cb _select] |
| 123 | bind $w_filter <Key-Return> [list focus $w_list]\;break |
| 124 | bind $w_filter <Key-Down> [list focus $w_list] |
| 125 | |
| 126 | set fmt list |
| 127 | append fmt { %(refname)} |
| 128 | append fmt { [list} |
| 129 | append fmt { %(objecttype)} |
| 130 | append fmt { %(objectname)} |
| 131 | append fmt { [concat %(taggername) %(authorname)]} |
| 132 | append fmt { [reformat_date [concat %(taggerdate) %(authordate)]]} |
| 133 | append fmt { %(subject)} |
| 134 | append fmt {] [list} |
| 135 | append fmt { %(*objecttype)} |
| 136 | append fmt { %(*objectname)} |
| 137 | append fmt { %(*authorname)} |
| 138 | append fmt { [reformat_date %(*authordate)]} |
| 139 | append fmt { %(*subject)} |
| 140 | append fmt {]} |
| 141 | set all_refn [list] |
| 142 | set fr_fd [git_read [list for-each-ref \ |
| 143 | --tcl \ |
| 144 | --sort=-taggerdate \ |
| 145 | --format=$fmt \ |
| 146 | refs/heads \ |
| 147 | refs/remotes \ |
| 148 | refs/tags \ |
| 149 | ]] |
| 150 | fconfigure $fr_fd -encoding utf-8 |
| 151 | while {[gets $fr_fd line] > 0} { |
| 152 | set line [eval $line] |
| 153 | if {[lindex $line 1 0] eq {tag}} { |
| 154 | if {[lindex $line 2 0] eq {commit}} { |
| 155 | set sha1 [lindex $line 2 1] |
| 156 | } else { |
| 157 | continue |
| 158 | } |
| 159 | } elseif {[lindex $line 1 0] eq {commit}} { |
| 160 | set sha1 [lindex $line 1 1] |
| 161 | } else { |
| 162 | continue |
| 163 | } |
| 164 | set refn [lindex $line 0] |
| 165 | set tip_data($refn) [lrange $line 1 end] |
| 166 | lappend cmt_refn($sha1) $refn |
| 167 | lappend all_refn $refn |
| 168 | } |
| 169 | close $fr_fd |
| 170 | |
| 171 | if {$unmerged_only} { |
| 172 | set fr_fd [git_read [list rev-list --all ^$::HEAD]] |
| 173 | while {[gets $fr_fd sha1] > 0} { |
| 174 | if {[catch {set rlst $cmt_refn($sha1)}]} continue |
| 175 | foreach refn $rlst { |
| 176 | set inc($refn) 1 |
| 177 | } |
| 178 | } |
| 179 | close $fr_fd |
| 180 | } else { |
| 181 | foreach refn $all_refn { |
| 182 | set inc($refn) 1 |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | set spec_head [list] |
| 187 | foreach name [load_all_heads] { |
| 188 | set refn refs/heads/$name |
| 189 | if {[info exists inc($refn)]} { |
| 190 | lappend spec_head [list $name $refn] |
| 191 | } |
| 192 | } |
| 193 | |
| 194 | set spec_trck [list] |
| 195 | foreach spec [all_tracking_branches] { |
| 196 | set refn [lindex $spec 0] |
| 197 | if {[info exists inc($refn)]} { |
| 198 | regsub ^refs/(heads|remotes)/ $refn {} name |
| 199 | lappend spec_trck [concat $name $spec] |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | set spec_tag [list] |
| 204 | foreach name [load_all_tags] { |
| 205 | set refn refs/tags/$name |
| 206 | if {[info exists inc($refn)]} { |
| 207 | lappend spec_tag [list $name $refn] |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | if {$is_detached} { set revtype HEAD |
| 212 | } elseif {[llength $spec_head] > 0} { set revtype head |
| 213 | } elseif {[llength $spec_trck] > 0} { set revtype trck |
| 214 | } elseif {[llength $spec_tag ] > 0} { set revtype tag |
| 215 | } else { set revtype expr |
| 216 | } |
| 217 | |
| 218 | if {$revtype eq {head} && $current_branch ne {}} { |
| 219 | set i 0 |
| 220 | foreach spec $spec_head { |
| 221 | if {[lindex $spec 0] eq $current_branch} { |
| 222 | $w_list selection clear 0 end |
| 223 | $w_list selection set $i |
| 224 | break |
| 225 | } |
| 226 | incr i |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | return $this |
| 231 | } |
| 232 | |
| 233 | method none {text} { |
| 234 | if {![winfo exists $w.none_r]} { |
| 235 | ttk::radiobutton $w.none_r \ |
| 236 | -value none \ |
| 237 | -variable @revtype |
| 238 | grid $w.none_r -sticky we -padx {0 5} -columnspan 2 |
| 239 | } |
| 240 | $w.none_r configure -text $text |
| 241 | } |
| 242 | |
| 243 | method get {} { |
| 244 | switch -- $revtype { |
| 245 | head - |
| 246 | trck - |
| 247 | tag { |
| 248 | set i [$w_list curselection] |
| 249 | if {$i ne {}} { |
| 250 | return [lindex $cur_specs $i 0] |
| 251 | } else { |
| 252 | return {} |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | HEAD { return HEAD } |
| 257 | expr { return $c_expr } |
| 258 | none { return {} } |
| 259 | default { error "unknown type of revision" } |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | method pick_tracking_branch {} { |
| 264 | set revtype trck |
| 265 | } |
| 266 | |
| 267 | method focus_filter {} { |
| 268 | if {[$w_filter cget -state] eq {normal}} { |
| 269 | focus $w_filter |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | method bind_listbox {event script} { |
| 274 | bind $w_list $event $script |
| 275 | } |
| 276 | |
| 277 | method get_local_branch {} { |
| 278 | if {$revtype eq {head}} { |
| 279 | return [_expr $this] |
| 280 | } else { |
| 281 | return {} |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | method get_tracking_branch {} { |
| 286 | set i [$w_list curselection] |
| 287 | if {$i eq {} || $revtype ne {trck}} { |
| 288 | return {} |
| 289 | } |
| 290 | return [lrange [lindex $cur_specs $i] 1 end] |
| 291 | } |
| 292 | |
| 293 | method get_commit {} { |
| 294 | set e [_expr $this] |
| 295 | if {$e eq {}} { |
| 296 | return {} |
| 297 | } |
| 298 | return [git rev-parse --verify "$e^0"] |
| 299 | } |
| 300 | |
| 301 | method commit_or_die {} { |
| 302 | if {[catch {set new [get_commit $this]} err]} { |
| 303 | |
| 304 | # Cleanup the not-so-friendly error from rev-parse. |
| 305 | # |
| 306 | regsub {^fatal:\s*} $err {} err |
| 307 | if {$err eq {Needed a single revision}} { |
| 308 | set err {} |
| 309 | } |
| 310 | |
| 311 | set top [winfo toplevel $w] |
| 312 | set msg [strcat [mc "Invalid revision: %s" [get $this]] "\n\n$err"] |
| 313 | tk_messageBox \ |
| 314 | -icon error \ |
| 315 | -type ok \ |
| 316 | -title [wm title $top] \ |
| 317 | -parent $top \ |
| 318 | -message $msg |
| 319 | error $msg |
| 320 | } |
| 321 | return $new |
| 322 | } |
| 323 | |
| 324 | method _expr {} { |
| 325 | switch -- $revtype { |
| 326 | head - |
| 327 | trck - |
| 328 | tag { |
| 329 | set i [$w_list curselection] |
| 330 | if {$i ne {}} { |
| 331 | return [lindex $cur_specs $i 1] |
| 332 | } else { |
| 333 | error [mc "No revision selected."] |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | expr { |
| 338 | if {$c_expr ne {}} { |
| 339 | return $c_expr |
| 340 | } else { |
| 341 | error [mc "Revision expression is empty."] |
| 342 | } |
| 343 | } |
| 344 | HEAD { return HEAD } |
| 345 | none { return {} } |
| 346 | default { error "unknown type of revision" } |
| 347 | } |
| 348 | } |
| 349 | |
| 350 | method _validate {d S} { |
| 351 | if {$d == 1} { |
| 352 | if {[regexp {\s} $S]} { |
| 353 | return 0 |
| 354 | } |
| 355 | if {[string length $S] > 0} { |
| 356 | set revtype expr |
| 357 | } |
| 358 | } |
| 359 | return 1 |
| 360 | } |
| 361 | |
| 362 | method _filter {P} { |
| 363 | if {[regexp {\s} $P]} { |
| 364 | return 0 |
| 365 | } |
| 366 | _rebuild $this $P |
| 367 | return 1 |
| 368 | } |
| 369 | |
| 370 | method _select {args} { |
| 371 | _rebuild $this $filter |
| 372 | focus_filter $this |
| 373 | } |
| 374 | |
| 375 | method _rebuild {pat} { |
| 376 | set ste normal |
| 377 | switch -- $revtype { |
| 378 | head { set new $spec_head } |
| 379 | trck { set new $spec_trck } |
| 380 | tag { set new $spec_tag } |
| 381 | expr - |
| 382 | HEAD - |
| 383 | none { |
| 384 | set new [list] |
| 385 | set ste disabled |
| 386 | } |
| 387 | } |
| 388 | |
| 389 | if {[$w_list cget -state] eq {disabled}} { |
| 390 | $w_list configure -state normal |
| 391 | } |
| 392 | $w_list delete 0 end |
| 393 | |
| 394 | if {$pat ne {}} { |
| 395 | set pat *${pat}* |
| 396 | } |
| 397 | set cur_specs [list] |
| 398 | foreach spec $new { |
| 399 | set txt [lindex $spec 0] |
| 400 | if {$pat eq {} || [string match $pat $txt]} { |
| 401 | lappend cur_specs $spec |
| 402 | $w_list insert end $txt |
| 403 | } |
| 404 | } |
| 405 | if {$cur_specs ne {}} { |
| 406 | $w_list selection clear 0 end |
| 407 | $w_list selection set 0 |
| 408 | } |
| 409 | |
| 410 | if {[$w_filter cget -state] ne $ste} { |
| 411 | $w_list configure -state $ste |
| 412 | $w_filter configure -state $ste |
| 413 | } |
| 414 | } |
| 415 | |
| 416 | method _delete {current} { |
| 417 | if {$current eq $w} { |
| 418 | delete_this |
| 419 | } |
| 420 | } |
| 421 | |
| 422 | method _sb_set {sb orient first last} { |
| 423 | set old_focus [focus -lastfor $w] |
| 424 | |
| 425 | if {$first == 0 && $last == 1} { |
| 426 | if {[winfo exists $sb]} { |
| 427 | destroy $sb |
| 428 | if {$old_focus ne {}} { |
| 429 | update |
| 430 | focus $old_focus |
| 431 | } |
| 432 | } |
| 433 | return |
| 434 | } |
| 435 | |
| 436 | if {![winfo exists $sb]} { |
| 437 | if {$orient eq {h}} { |
| 438 | ttk::scrollbar $sb -orient h -command [list $w_list xview] |
| 439 | pack $sb -fill x -side bottom -before $w_list |
| 440 | } else { |
| 441 | ttk::scrollbar $sb -orient v -command [list $w_list yview] |
| 442 | pack $sb -fill y -side right -before $w_list |
| 443 | } |
| 444 | if {$old_focus ne {}} { |
| 445 | update |
| 446 | focus $old_focus |
| 447 | } |
| 448 | } |
| 449 | |
| 450 | catch {$sb set $first $last} |
| 451 | } |
| 452 | |
| 453 | method _show_tooltip {pos} { |
| 454 | if {$tooltip_wm ne {}} { |
| 455 | _open_tooltip $this |
| 456 | } elseif {$tooltip_timer eq {}} { |
| 457 | set tooltip_timer [after 1000 [cb _open_tooltip]] |
| 458 | } |
| 459 | } |
| 460 | |
| 461 | method _open_tooltip {} { |
| 462 | global remote_url |
| 463 | |
| 464 | set tooltip_timer {} |
| 465 | set pos_x [winfo pointerx $w_list] |
| 466 | set pos_y [winfo pointery $w_list] |
| 467 | if {[winfo containing $pos_x $pos_y] ne $w_list} { |
| 468 | _hide_tooltip $this |
| 469 | return |
| 470 | } |
| 471 | |
| 472 | set pos @[join [list \ |
| 473 | [expr {$pos_x - [winfo rootx $w_list]}] \ |
| 474 | [expr {$pos_y - [winfo rooty $w_list]}]] ,] |
| 475 | set lno [$w_list index $pos] |
| 476 | if {$lno eq {}} { |
| 477 | _hide_tooltip $this |
| 478 | return |
| 479 | } |
| 480 | |
| 481 | set spec [lindex $cur_specs $lno] |
| 482 | set refn [lindex $spec 1] |
| 483 | if {$refn eq {}} { |
| 484 | _hide_tooltip $this |
| 485 | return |
| 486 | } |
| 487 | |
| 488 | if {$tooltip_wm eq {}} { |
| 489 | set tooltip_wm [toplevel $w_list.tooltip -borderwidth 1] |
| 490 | catch {wm attributes $tooltip_wm -type tooltip} |
| 491 | wm overrideredirect $tooltip_wm 1 |
| 492 | wm transient $tooltip_wm [winfo toplevel $w_list] |
| 493 | set tooltip_t $tooltip_wm.label |
| 494 | text $tooltip_t \ |
| 495 | -takefocus 0 \ |
| 496 | -highlightthickness 0 \ |
| 497 | -relief flat \ |
| 498 | -borderwidth 0 \ |
| 499 | -wrap none \ |
| 500 | -background lightyellow \ |
| 501 | -foreground black |
| 502 | $tooltip_t tag conf section_header -font font_uibold |
| 503 | bind $tooltip_wm <Escape> [cb _hide_tooltip] |
| 504 | pack $tooltip_t |
| 505 | } else { |
| 506 | $tooltip_t conf -state normal |
| 507 | $tooltip_t delete 0.0 end |
| 508 | } |
| 509 | |
| 510 | set data $tip_data($refn) |
| 511 | if {[lindex $data 0 0] eq {tag}} { |
| 512 | set tag [lindex $data 0] |
| 513 | if {[lindex $data 1 0] eq {commit}} { |
| 514 | set cmit [lindex $data 1] |
| 515 | } else { |
| 516 | set cmit {} |
| 517 | } |
| 518 | } elseif {[lindex $data 0 0] eq {commit}} { |
| 519 | set tag {} |
| 520 | set cmit [lindex $data 0] |
| 521 | } |
| 522 | |
| 523 | $tooltip_t insert end [lindex $spec 0] |
| 524 | set last [_reflog_last $this [lindex $spec 1]] |
| 525 | if {$last ne {}} { |
| 526 | $tooltip_t insert end "\n" |
| 527 | $tooltip_t insert end [mc "Updated"] |
| 528 | $tooltip_t insert end " $last" |
| 529 | } |
| 530 | $tooltip_t insert end "\n" |
| 531 | |
| 532 | if {$tag ne {}} { |
| 533 | $tooltip_t insert end "\n" |
| 534 | $tooltip_t insert end [mc "Tag"] section_header |
| 535 | $tooltip_t insert end " [lindex $tag 1]\n" |
| 536 | $tooltip_t insert end [lindex $tag 2] |
| 537 | $tooltip_t insert end " ([lindex $tag 3])\n" |
| 538 | $tooltip_t insert end [lindex $tag 4] |
| 539 | $tooltip_t insert end "\n" |
| 540 | } |
| 541 | |
| 542 | if {$cmit ne {}} { |
| 543 | $tooltip_t insert end "\n" |
| 544 | $tooltip_t insert end [mc "Commit@@noun"] section_header |
| 545 | $tooltip_t insert end " [lindex $cmit 1]\n" |
| 546 | $tooltip_t insert end [lindex $cmit 2] |
| 547 | $tooltip_t insert end " ([lindex $cmit 3])\n" |
| 548 | $tooltip_t insert end [lindex $cmit 4] |
| 549 | } |
| 550 | |
| 551 | if {[llength $spec] > 2} { |
| 552 | $tooltip_t insert end "\n" |
| 553 | $tooltip_t insert end [mc "Remote"] section_header |
| 554 | $tooltip_t insert end " [lindex $spec 2]\n" |
| 555 | $tooltip_t insert end [mc "URL"] |
| 556 | $tooltip_t insert end " $remote_url([lindex $spec 2])\n" |
| 557 | $tooltip_t insert end [mc "Branch"] |
| 558 | $tooltip_t insert end " [lindex $spec 3]" |
| 559 | } |
| 560 | |
| 561 | $tooltip_t conf -state disabled |
| 562 | _position_tooltip $this |
| 563 | } |
| 564 | |
| 565 | method _reflog_last {name} { |
| 566 | if {[info exists reflog_last($name)]} { |
| 567 | return reflog_last($name) |
| 568 | } |
| 569 | |
| 570 | set last {} |
| 571 | if {[catch {set last [file mtime [gitdir $name]]}] |
| 572 | && ![catch {set g [safe_open_file [gitdir logs $name] r]}]} { |
| 573 | fconfigure $g -encoding iso8859-1 |
| 574 | while {[gets $g line] >= 0} { |
| 575 | if {[regexp {> ([1-9][0-9]*) } $line line when]} { |
| 576 | set last $when |
| 577 | } |
| 578 | } |
| 579 | close $g |
| 580 | } |
| 581 | |
| 582 | if {$last ne {}} { |
| 583 | set last [format_date $last] |
| 584 | } |
| 585 | set reflog_last($name) $last |
| 586 | return $last |
| 587 | } |
| 588 | |
| 589 | method _position_tooltip {} { |
| 590 | set max_h [lindex [split [$tooltip_t index end] .] 0] |
| 591 | set max_w 0 |
| 592 | for {set i 1} {$i <= $max_h} {incr i} { |
| 593 | set c [lindex [split [$tooltip_t index "$i.0 lineend"] .] 1] |
| 594 | if {$c > $max_w} {set max_w $c} |
| 595 | } |
| 596 | $tooltip_t conf -width $max_w -height $max_h |
| 597 | |
| 598 | set req_w [winfo reqwidth $tooltip_t] |
| 599 | set req_h [winfo reqheight $tooltip_t] |
| 600 | set pos_x [expr {[winfo pointerx .] + 5}] |
| 601 | set pos_y [expr {[winfo pointery .] + 10}] |
| 602 | |
| 603 | set g "${req_w}x${req_h}" |
| 604 | if {[tk windowingsystem] eq "win32" || $pos_x >= 0} {append g +} |
| 605 | append g $pos_x |
| 606 | if {[tk windowingsystem] eq "win32" || $pos_y >= 0} {append g +} |
| 607 | append g $pos_y |
| 608 | |
| 609 | wm geometry $tooltip_wm $g |
| 610 | raise $tooltip_wm |
| 611 | } |
| 612 | |
| 613 | method _hide_tooltip {} { |
| 614 | if {$tooltip_wm ne {}} { |
| 615 | destroy $tooltip_wm |
| 616 | set tooltip_wm {} |
| 617 | } |
| 618 | if {$tooltip_timer ne {}} { |
| 619 | after cancel $tooltip_timer |
| 620 | set tooltip_timer {} |
| 621 | } |
| 622 | } |
| 623 | |
| 624 | } |