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