1aab56f5cc18de72d43af88b475c8c85d0f0dcb8
   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 get_local_branch {} {
 189        if {$revtype eq {head}} {
 190                return [_expr $this]
 191        } else {
 192                return {}
 193        }
 194}
 195
 196method get_tracking_branch {} {
 197        set i [$w_list curselection]
 198        if {$i eq {} || $revtype ne {trck}} {
 199                return {}
 200        }
 201        return [lrange [lindex $cur_specs $i] 1 end]
 202}
 203
 204method get_commit {} {
 205        set e [_expr $this]
 206        if {$e eq {}} {
 207                return {}
 208        }
 209        return [git rev-parse --verify "$e^0"]
 210}
 211
 212method commit_or_die {} {
 213        if {[catch {set new [get_commit $this]} err]} {
 214
 215                # Cleanup the not-so-friendly error from rev-parse.
 216                #
 217                regsub {^fatal:\s*} $err {} err
 218                if {$err eq {Needed a single revision}} {
 219                        set err {}
 220                }
 221
 222                set top [winfo toplevel $w]
 223                set msg "Invalid revision: [get $this]\n\n$err"
 224                tk_messageBox \
 225                        -icon error \
 226                        -type ok \
 227                        -title [wm title $top] \
 228                        -parent $top \
 229                        -message $msg
 230                error $msg
 231        }
 232        return $new
 233}
 234
 235method _expr {} {
 236        switch -- $revtype {
 237        head -
 238        trck -
 239        tag  {
 240                set i [$w_list curselection]
 241                if {$i ne {}} {
 242                        return [lindex $cur_specs $i 1]
 243                } else {
 244                        error "No revision selected."
 245                }
 246        }
 247
 248        expr {
 249                if {$c_expr ne {}} {
 250                        return $c_expr
 251                } else {
 252                        error "Revision expression is empty."
 253                }
 254        }
 255        HEAD { return HEAD                     }
 256        none { return {}                       }
 257        default { error "unknown type of revision"      }
 258        }
 259}
 260
 261method _validate {d S} {
 262        if {$d == 1} {
 263                if {[regexp {\s} $S]} {
 264                        return 0
 265                }
 266                if {[string length $S] > 0} {
 267                        set revtype expr
 268                }
 269        }
 270        return 1
 271}
 272
 273method _filter {P} {
 274        if {[regexp {\s} $P]} {
 275                return 0
 276        }
 277        _rebuild $this $P
 278        return 1
 279}
 280
 281method _select {args} {
 282        _rebuild $this $filter
 283        focus_filter $this
 284}
 285
 286method _rebuild {pat} {
 287        set ste normal
 288        switch -- $revtype {
 289        head { set new $spec_head }
 290        trck { set new $spec_trck }
 291        tag  { set new $spec_tag  }
 292        expr -
 293        HEAD -
 294        none {
 295                set new [list]
 296                set ste disabled
 297        }
 298        }
 299
 300        if {[$w_list cget -state] eq {disabled}} {
 301                $w_list configure -state normal
 302        }
 303        $w_list delete 0 end
 304
 305        if {$pat ne {}} {
 306                set pat *${pat}*
 307        }
 308        set cur_specs [list]
 309        foreach spec $new {
 310                set txt [lindex $spec 0]
 311                if {$pat eq {} || [string match $pat $txt]} {
 312                        lappend cur_specs $spec
 313                        $w_list insert end $txt
 314                }
 315        }
 316        if {$cur_specs ne {}} {
 317                $w_list selection clear 0 end
 318                $w_list selection set 0
 319        }
 320
 321        if {[$w_filter cget -state] ne $ste} {
 322                $w_list   configure -state $ste
 323                $w_filter configure -state $ste
 324        }
 325}
 326
 327method _delete {current} {
 328        if {$current eq $w} {
 329                delete_this
 330        }
 331}
 332
 333method _sb_set {sb orient first last} {
 334        set old_focus [focus -lastfor $w]
 335
 336        if {$first == 0 && $last == 1} {
 337                if {[winfo exists $sb]} {
 338                        destroy $sb
 339                        if {$old_focus ne {}} {
 340                                update
 341                                focus $old_focus
 342                        }
 343                }
 344                return
 345        }
 346
 347        if {![winfo exists $sb]} {
 348                if {$orient eq {h}} {
 349                        scrollbar $sb -orient h -command [list $w_list xview]
 350                        pack $sb -fill x -side bottom -before $w_list
 351                } else {
 352                        scrollbar $sb -orient v -command [list $w_list yview]
 353                        pack $sb -fill y -side right -before $w_list
 354                }
 355                if {$old_focus ne {}} {
 356                        update
 357                        focus $old_focus
 358                }
 359        }
 360        $sb set $first $last
 361}
 362
 363}