123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717 |
- # iconlist.tcl
- #
- # Implements the icon-list megawidget used in the "Tk" standard file
- # selection dialog boxes.
- #
- # Copyright (c) 1994-1998 Sun Microsystems, Inc.
- # Copyright (c) 2009 Donal K. Fellows
- #
- # See the file "license.terms" for information on usage and redistribution of
- # this file, and for a DISCLAIMER OF ALL WARRANTIES.
- #
- # API Summary:
- # tk::IconList <path> ?<option> <value>? ...
- # <path> add <imageName> <itemList>
- # <path> cget <option>
- # <path> configure ?<option>? ?<value>? ...
- # <path> deleteall
- # <path> destroy
- # <path> get <itemIndex>
- # <path> index <index>
- # <path> invoke
- # <path> see <index>
- # <path> selection anchor ?<int>?
- # <path> selection clear <first> ?<last>?
- # <path> selection get
- # <path> selection includes <item>
- # <path> selection set <first> ?<last>?
- package require Tk
- ::tk::Megawidget create ::tk::IconList ::tk::FocusableWidget {
- variable w canvas sbar accel accelCB fill font index \
- itemList itemsPerColumn list maxIH maxIW maxTH maxTW noScroll \
- numItems oldX oldY options rect selected selection textList
- constructor args {
- next {*}$args
- set accelCB {}
- }
- destructor {
- my Reset
- next
- }
- method GetSpecs {} {
- concat [next] {
- {-command "" "" ""}
- {-font "" "" "TkIconFont"}
- {-multiple "" "" "0"}
- }
- }
- # ----------------------------------------------------------------------
- method index i {
- if {![info exist list]} {
- set list {}
- }
- switch -regexp -- $i {
- "^-?[0-9]+$" {
- if {$i < 0} {
- set i 0
- }
- if {$i >= [llength $list]} {
- set i [expr {[llength $list] - 1}]
- }
- return $i
- }
- "^anchor$" {
- return $index(anchor)
- }
- "^end$" {
- return [llength $list]
- }
- "@-?[0-9]+,-?[0-9]+" {
- scan $i "@%d,%d" x y
- set item [$canvas find closest \
- [$canvas canvasx $x] [$canvas canvasy $y]]
- return [lindex [$canvas itemcget $item -tags] 1]
- }
- }
- }
- method selection {op args} {
- switch -exact -- $op {
- anchor {
- if {[llength $args] == 1} {
- set index(anchor) [$w index [lindex $args 0]]
- } else {
- return $index(anchor)
- }
- }
- clear {
- switch [llength $args] {
- 2 {
- lassign $args first last
- }
- 1 {
- set first [set last [lindex $args 0]]
- }
- default {
- return -code error -errorcode {TCL WRONGARGS} \
- "wrong # args: should be\
- \"[lrange [info level 0] 0 1] first ?last?\""
- }
- }
- set first [$w index $first]
- set last [$w index $last]
- if {$first > $last} {
- set tmp $first
- set first $last
- set last $tmp
- }
- set ind 0
- foreach item $selection {
- if {$item >= $first} {
- set first $ind
- break
- }
- incr ind
- }
- set ind [expr {[llength $selection] - 1}]
- for {} {$ind >= 0} {incr ind -1} {
- set item [lindex $selection $ind]
- if {$item <= $last} {
- set last $ind
- break
- }
- }
- if {$first > $last} {
- return
- }
- set selection [lreplace $selection $first $last]
- event generate $w <<ListboxSelect>>
- my DrawSelection
- }
- get {
- return $selection
- }
- includes {
- return [expr {[lindex $args 0] in $selection}]
- }
- set {
- switch [llength $args] {
- 2 {
- lassign $args first last
- }
- 1 {
- set first [set last [lindex $args 0]]
- }
- default {
- return -code error -errorcode {TCL WRONGARGS} \
- "wrong # args: should be\
- \"[lrange [info level 0] 0 1] first ?last?\""
- }
- }
- set first [$w index $first]
- set last [$w index $last]
- if {$first > $last} {
- set tmp $first
- set first $last
- set last $tmp
- }
- for {set i $first} {$i <= $last} {incr i} {
- lappend selection $i
- }
- set selection [lsort -integer -unique $selection]
- event generate $w <<ListboxSelect>>
- my DrawSelection
- }
- }
- }
- method get item {
- set rTag [lindex $list $item 2]
- lassign $itemList($rTag) iTag tTag text serial
- return $text
- }
- # Deletes all the items inside the canvas subwidget and reset the
- # iconList's state.
- #
- method deleteall {} {
- $canvas delete all
- unset -nocomplain selected rect list itemList
- set maxIW 1
- set maxIH 1
- set maxTW 1
- set maxTH 1
- set numItems 0
- set noScroll 1
- set selection {}
- set index(anchor) ""
- $sbar set 0.0 1.0
- $canvas xview moveto 0
- }
- # Adds an icon into the IconList with the designated image and text
- #
- method add {image items} {
- foreach text $items {
- set iID item$numItems
- set iTag [$canvas create image 0 0 -image $image -anchor nw \
- -tags [list icon $numItems $iID]]
- set tTag [$canvas create text 0 0 -text $text -anchor nw \
- -font $options(-font) -fill $fill \
- -tags [list text $numItems $iID]]
- set rTag [$canvas create rect 0 0 0 0 -fill "" -outline "" \
- -tags [list rect $numItems $iID]]
- lassign [$canvas bbox $iTag] x1 y1 x2 y2
- set iW [expr {$x2 - $x1}]
- set iH [expr {$y2 - $y1}]
- if {$maxIW < $iW} {
- set maxIW $iW
- }
- if {$maxIH < $iH} {
- set maxIH $iH
- }
- lassign [$canvas bbox $tTag] x1 y1 x2 y2
- set tW [expr {$x2 - $x1}]
- set tH [expr {$y2 - $y1}]
- if {$maxTW < $tW} {
- set maxTW $tW
- }
- if {$maxTH < $tH} {
- set maxTH $tH
- }
- lappend list [list $iTag $tTag $rTag $iW $iH $tW $tH $numItems]
- set itemList($rTag) [list $iTag $tTag $text $numItems]
- set textList($numItems) [string tolower $text]
- incr numItems
- }
- my WhenIdle Arrange
- return
- }
- # Gets called when the user invokes the IconList (usually by
- # double-clicking or pressing the Return key).
- #
- method invoke {} {
- if {$options(-command) ne "" && [llength $selection]} {
- uplevel #0 $options(-command)
- }
- }
- # If the item is not (completely) visible, scroll the canvas so that it
- # becomes visible.
- #
- method see rTag {
- if {$noScroll} {
- return
- }
- set sRegion [$canvas cget -scrollregion]
- if {$sRegion eq ""} {
- return
- }
- if {$rTag < 0 || $rTag >= [llength $list]} {
- return
- }
- set bbox [$canvas bbox item$rTag]
- set pad [expr {[$canvas cget -highlightthickness]+[$canvas cget -bd]}]
- set x1 [lindex $bbox 0]
- set x2 [lindex $bbox 2]
- incr x1 [expr {$pad * -2}]
- incr x2 [expr {$pad * -1}]
- set cW [expr {[winfo width $canvas] - $pad*2}]
- set scrollW [expr {[lindex $sRegion 2]-[lindex $sRegion 0]+1}]
- set dispX [expr {int([lindex [$canvas xview] 0]*$scrollW)}]
- set oldDispX $dispX
- # check if out of the right edge
- #
- if {($x2 - $dispX) >= $cW} {
- set dispX [expr {$x2 - $cW}]
- }
- # check if out of the left edge
- #
- if {($x1 - $dispX) < 0} {
- set dispX $x1
- }
- if {$oldDispX ne $dispX} {
- set fraction [expr {double($dispX) / double($scrollW)}]
- $canvas xview moveto $fraction
- }
- }
- # ----------------------------------------------------------------------
- # Places the icons in a column-major arrangement.
- #
- method Arrange {} {
- if {![info exists list]} {
- if {[info exists canvas] && [winfo exists $canvas]} {
- set noScroll 1
- $sbar configure -command ""
- }
- return
- }
- set W [winfo width $canvas]
- set H [winfo height $canvas]
- set pad [expr {[$canvas cget -highlightthickness]+[$canvas cget -bd]}]
- if {$pad < 2} {
- set pad 2
- }
- incr W [expr {$pad*-2}]
- incr H [expr {$pad*-2}]
- set dx [expr {$maxIW + $maxTW + 8}]
- if {$maxTH > $maxIH} {
- set dy $maxTH
- } else {
- set dy $maxIH
- }
- incr dy 2
- set shift [expr {$maxIW + 4}]
- set x [expr {$pad * 2}]
- set y [expr {$pad * 1}] ; # Why * 1 ?
- set usedColumn 0
- foreach sublist $list {
- set usedColumn 1
- lassign $sublist iTag tTag rTag iW iH tW tH
- set i_dy [expr {($dy - $iH)/2}]
- set t_dy [expr {($dy - $tH)/2}]
- $canvas coords $iTag $x [expr {$y + $i_dy}]
- $canvas coords $tTag [expr {$x + $shift}] [expr {$y + $t_dy}]
- $canvas coords $rTag $x $y [expr {$x+$dx}] [expr {$y+$dy}]
- incr y $dy
- if {($y + $dy) > $H} {
- set y [expr {$pad * 1}] ; # *1 ?
- incr x $dx
- set usedColumn 0
- }
- }
- if {$usedColumn} {
- set sW [expr {$x + $dx}]
- } else {
- set sW $x
- }
- if {$sW < $W} {
- $canvas configure -scrollregion [list $pad $pad $sW $H]
- $sbar configure -command ""
- $canvas xview moveto 0
- set noScroll 1
- } else {
- $canvas configure -scrollregion [list $pad $pad $sW $H]
- $sbar configure -command [list $canvas xview]
- set noScroll 0
- }
- set itemsPerColumn [expr {($H-$pad) / $dy}]
- if {$itemsPerColumn < 1} {
- set itemsPerColumn 1
- }
- my DrawSelection
- }
- method DrawSelection {} {
- $canvas delete selection
- $canvas itemconfigure selectionText -fill black
- $canvas dtag selectionText
- set cbg [ttk::style lookup TEntry -selectbackground focus]
- set cfg [ttk::style lookup TEntry -selectforeground focus]
- foreach item $selection {
- set rTag [lindex $list $item 2]
- foreach {iTag tTag text serial} $itemList($rTag) {
- break
- }
- set bbox [$canvas bbox $tTag]
- $canvas create rect $bbox -fill $cbg -outline $cbg \
- -tags selection
- $canvas itemconfigure $tTag -fill $cfg -tags selectionText
- }
- $canvas lower selection
- return
- }
- # Creates an IconList widget by assembling a canvas widget and a
- # scrollbar widget. Sets all the bindings necessary for the IconList's
- # operations.
- #
- method Create {} {
- variable hull
- set sbar [ttk::scrollbar $hull.sbar -orient horizontal -takefocus 0]
- catch {$sbar configure -highlightthickness 0}
- set canvas [canvas $hull.canvas -highlightthick 0 -takefocus 1 \
- -width 400 -height 120 -background white]
- pack $sbar -side bottom -fill x -padx 2 -pady {0 2}
- pack $canvas -expand yes -fill both -padx 2 -pady {2 0}
- $sbar configure -command [list $canvas xview]
- $canvas configure -xscrollcommand [list $sbar set]
- # Initializes the max icon/text width and height and other variables
- #
- set maxIW 1
- set maxIH 1
- set maxTW 1
- set maxTH 1
- set numItems 0
- set noScroll 1
- set selection {}
- set index(anchor) ""
- set fg [option get $canvas foreground Foreground]
- if {$fg eq ""} {
- set fill black
- } else {
- set fill $fg
- }
- # Creates the event bindings.
- #
- bind $canvas <Configure> [namespace code {my WhenIdle Arrange}]
- bind $canvas <1> [namespace code {my Btn1 %x %y}]
- bind $canvas <B1-Motion> [namespace code {my Motion1 %x %y}]
- bind $canvas <B1-Leave> [namespace code {my Leave1 %x %y}]
- bind $canvas <Control-1> [namespace code {my CtrlBtn1 %x %y}]
- bind $canvas <Shift-1> [namespace code {my ShiftBtn1 %x %y}]
- bind $canvas <B1-Enter> [list tk::CancelRepeat]
- bind $canvas <ButtonRelease-1> [list tk::CancelRepeat]
- bind $canvas <Double-ButtonRelease-1> \
- [namespace code {my Double1 %x %y}]
- bind $canvas <Control-B1-Motion> {;}
- bind $canvas <Shift-B1-Motion> [namespace code {my ShiftMotion1 %x %y}]
- if {[tk windowingsystem] eq "aqua"} {
- bind $canvas <Shift-MouseWheel> [namespace code {my MouseWheel [expr {40 * (%D)}]}]
- bind $canvas <Option-Shift-MouseWheel> [namespace code {my MouseWheel [expr {400 * (%D)}]}]
- } else {
- bind $canvas <Shift-MouseWheel> [namespace code {my MouseWheel %D}]
- }
- if {[tk windowingsystem] eq "x11"} {
- bind $canvas <Shift-4> [namespace code {my MouseWheel 120}]
- bind $canvas <Shift-5> [namespace code {my MouseWheel -120}]
- }
- bind $canvas <<PrevLine>> [namespace code {my UpDown -1}]
- bind $canvas <<NextLine>> [namespace code {my UpDown 1}]
- bind $canvas <<PrevChar>> [namespace code {my LeftRight -1}]
- bind $canvas <<NextChar>> [namespace code {my LeftRight 1}]
- bind $canvas <Return> [namespace code {my ReturnKey}]
- bind $canvas <KeyPress> [namespace code {my KeyPress %A}]
- bind $canvas <Control-KeyPress> ";"
- bind $canvas <Alt-KeyPress> ";"
- bind $canvas <FocusIn> [namespace code {my FocusIn}]
- bind $canvas <FocusOut> [namespace code {my FocusOut}]
- return $w
- }
- # This procedure is invoked when the mouse leaves an entry window with
- # button 1 down. It scrolls the window up, down, left, or right,
- # depending on where the mouse left the window, and reschedules itself
- # as an "after" command so that the window continues to scroll until the
- # mouse moves back into the window or the mouse button is released.
- #
- method AutoScan {} {
- if {![winfo exists $w]} return
- set x $oldX
- set y $oldY
- if {$noScroll} {
- return
- }
- if {$x >= [winfo width $canvas]} {
- $canvas xview scroll 1 units
- } elseif {$x < 0} {
- $canvas xview scroll -1 units
- } elseif {$y >= [winfo height $canvas]} {
- # do nothing
- } elseif {$y < 0} {
- # do nothing
- } else {
- return
- }
- my Motion1 $x $y
- set ::tk::Priv(afterId) [after 50 [namespace code {my AutoScan}]]
- }
- # ----------------------------------------------------------------------
- # Event handlers
- method MouseWheel {amount} {
- if {$noScroll || $::tk_strictMotif} {
- return
- }
- if {$amount > 0} {
- $canvas xview scroll [expr {(-119-$amount) / 120}] units
- } else {
- $canvas xview scroll [expr {-($amount / 120)}] units
- }
- }
- method Btn1 {x y} {
- focus $canvas
- set i [$w index @$x,$y]
- if {$i eq ""} {
- return
- }
- $w selection clear 0 end
- $w selection set $i
- $w selection anchor $i
- }
- method CtrlBtn1 {x y} {
- if {$options(-multiple)} {
- focus $canvas
- set i [$w index @$x,$y]
- if {$i eq ""} {
- return
- }
- if {[$w selection includes $i]} {
- $w selection clear $i
- } else {
- $w selection set $i
- $w selection anchor $i
- }
- }
- }
- method ShiftBtn1 {x y} {
- if {$options(-multiple)} {
- focus $canvas
- set i [$w index @$x,$y]
- if {$i eq ""} {
- return
- }
- if {[$w index anchor] eq ""} {
- $w selection anchor $i
- }
- $w selection clear 0 end
- $w selection set anchor $i
- }
- }
- # Gets called on button-1 motions
- #
- method Motion1 {x y} {
- set oldX $x
- set oldY $y
- set i [$w index @$x,$y]
- if {$i eq ""} {
- return
- }
- $w selection clear 0 end
- $w selection set $i
- }
- method ShiftMotion1 {x y} {
- set oldX $x
- set oldY $y
- set i [$w index @$x,$y]
- if {$i eq ""} {
- return
- }
- $w selection clear 0 end
- $w selection set anchor $i
- }
- method Double1 {x y} {
- if {[llength $selection]} {
- $w invoke
- }
- }
- method ReturnKey {} {
- $w invoke
- }
- method Leave1 {x y} {
- set oldX $x
- set oldY $y
- my AutoScan
- }
- method FocusIn {} {
- $w state focus
- if {![info exists list]} {
- return
- }
- if {[llength $selection]} {
- my DrawSelection
- }
- }
- method FocusOut {} {
- $w state !focus
- $w selection clear 0 end
- }
- # Moves the active element up or down by one element
- #
- # Arguments:
- # amount - +1 to move down one item, -1 to move back one item.
- #
- method UpDown amount {
- if {![info exists list]} {
- return
- }
- set curr [$w selection get]
- if {[llength $curr] == 0} {
- set i 0
- } else {
- set i [$w index anchor]
- if {$i eq ""} {
- return
- }
- incr i $amount
- }
- $w selection clear 0 end
- $w selection set $i
- $w selection anchor $i
- $w see $i
- }
- # Moves the active element left or right by one column
- #
- # Arguments:
- # amount - +1 to move right one column, -1 to move left one
- # column
- #
- method LeftRight amount {
- if {![info exists list]} {
- return
- }
- set curr [$w selection get]
- if {[llength $curr] == 0} {
- set i 0
- } else {
- set i [$w index anchor]
- if {$i eq ""} {
- return
- }
- incr i [expr {$amount * $itemsPerColumn}]
- }
- $w selection clear 0 end
- $w selection set $i
- $w selection anchor $i
- $w see $i
- }
- # Gets called when user enters an arbitrary key in the listbox.
- #
- method KeyPress key {
- append accel $key
- my Goto $accel
- after cancel $accelCB
- set accelCB [after 500 [namespace code {my Reset}]]
- }
- method Goto text {
- if {![info exists list]} {
- return
- }
- if {$text eq "" || $numItems == 0} {
- return
- }
- if {[llength [$w selection get]]} {
- set start [$w index anchor]
- } else {
- set start 0
- }
- set theIndex -1
- set less 0
- set len [string length $text]
- set len0 [expr {$len - 1}]
- set i $start
- # Search forward until we find a filename whose prefix is a
- # case-insensitive match with $text
- while {1} {
- if {[string equal -nocase -length $len0 $textList($i) $text]} {
- set theIndex $i
- break
- }
- incr i
- if {$i == $numItems} {
- set i 0
- }
- if {$i == $start} {
- break
- }
- }
- if {$theIndex >= 0} {
- $w selection clear 0 end
- $w selection set $theIndex
- $w selection anchor $theIndex
- $w see $theIndex
- }
- }
- method Reset {} {
- unset -nocomplain accel
- }
- }
- return
- # Local Variables:
- # mode: tcl
- # fill-column: 78
- # End:
|