diff options
Diffstat (limited to 'itcl/iwidgets3.0.0/generic/combobox.itk')
-rw-r--r-- | itcl/iwidgets3.0.0/generic/combobox.itk | 1339 |
1 files changed, 1339 insertions, 0 deletions
diff --git a/itcl/iwidgets3.0.0/generic/combobox.itk b/itcl/iwidgets3.0.0/generic/combobox.itk new file mode 100644 index 00000000000..45b79b037b6 --- /dev/null +++ b/itcl/iwidgets3.0.0/generic/combobox.itk @@ -0,0 +1,1339 @@ +# Combobox +# ---------------------------------------------------------------------- +# Implements a Combobox widget. A Combobox has 2 basic styles: simple and +# dropdown. Dropdowns display an entry field with an arrow button to the +# right of it. When the arrow button is pressed a selectable list of +# items is popped up. A simple Combobox displays an entry field and a listbox +# just beneath it which is always displayed. In both types, if the user +# selects an item in the listbox, the contents of the entry field are +# replaced with the text from the selected item. If the Combobox is +# editable, the user can type in the entry field and when <Return> is +# pressed the item will be inserted into the list. +# +# WISH LIST: +# This section lists possible future enhancements. +# +# Combobox 1.x: +# - convert bindings to bindtags. +# +# ---------------------------------------------------------------------- +# ORIGINAL AUTHOR: John S. Sigler EMAIL: jsigler@spd.dsccc.com +# sigler@onramp.net +# ---------------------------------------------------------------------- +# CURRENT MAINTAINER: Mitch Gorman EMAIL: logain@erols.com +# Copyright (c) 1995 John S. Sigler +# Copyright (c) 1997 Mitch Gorman +# ====================================================================== +# Permission is hereby granted, without written agreement and without +# license or royalty fees, to use, copy, modify, and distribute this +# software and its documentation for any purpose, provided that the +# above copyright notice and the following two paragraphs appear in +# all copies of this software. +# +# IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR +# DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +# ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN +# IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +# DAMAGE. +# +# THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS +# ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO +# PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +# ====================================================================== + +# +# Default resources. +# +option add *Combobox.borderWidth 2 widgetDefault +option add *Combobox.labelPos wn widgetDefault +option add *Combobox.listHeight 150 widgetDefault +option add *Combobox.hscrollMode dynamic widgetDefault +option add *Combobox.vscrollMode dynamic widgetDefault + +# +# Usual options. +# +itk::usual Combobox { + keep -background -borderwidth -cursor -foreground -highlightcolor \ + -highlightthickness -insertbackground -insertborderwidth \ + -insertofftime -insertontime -insertwidth -labelfont -popupcursor \ + -selectbackground -selectborderwidth -selectforeground \ + -textbackground -textfont +} + +# ------------------------------------------------------------------ +# COMBOBOX +# ------------------------------------------------------------------ +class iwidgets::Combobox { + inherit iwidgets::Entryfield + + constructor {args} {} + destructor {} + + itk_option define -arrowrelief arrowRelief Relief raised + itk_option define -completion completion Completion true + itk_option define -dropdown dropdown Dropdown true + itk_option define -editable editable Editable true + itk_option define -grab grab Grab local + itk_option define -listheight listHeight Height 150 + itk_option define -margin margin Margin 1 + itk_option define -popupcursor popupCursor Cursor arrow + itk_option define -selectioncommand selectionCommand SelectionCommand {} + itk_option define -state state State normal + itk_option define -unique unique Unique true + + public method clear {{component all}} + public method curselection {} + public method delete {component first {last {}}} + public method get {{index {}}} + public method getcurselection {} + public method insert {component index args} + public method invoke {} + public method justify {direction} + public method see {index} + public method selection {option first {last {}}} + public method size {} + public method sort {{mode ascending}} + public method xview {args} + public method yview {args} + + protected method _addToList {} + protected method _createComponents {} + protected method _deleteList {first {last {}}} + protected method _deleteText {first {last {}}} + protected method _doLayout {{when later}} + protected method _drawArrow {} + protected method _dropdownBtnRelease {{window {}} {x 1} {y 1}} + protected method _ignoreNextBtnRelease {ignore} + protected method _next {} + protected method _packComponents {{when later}} + protected method _positionList {} + protected method _postList {} + protected method _previous {} + protected method _resizeArrow {} + protected method _selectCmd {} + protected method _toggleList {} + protected method _unpostList {} + protected method _commonBindings {} + protected method _dropdownBindings {} + protected method _simpleBindings {} + protected method _listShowing {{val ""}} + + private method _bs {} + private method _lookup {key} + private method _slbListbox {} + private method _stateSelect {} + + private variable _doit 0; + private variable _inbs 0; + private variable _inlookup 0; + private variable _currItem {}; ;# current selected item. + private variable _ignoreRelease false ;# next button release ignored. + private variable _isPosted false; ;# is the dropdown popped up. + private variable _repacking {} ;# non-null => _packComponents pending. + private common _listShowing + private common count 0 +} + +# +# Provide a lowercase access method for the Combobox class. +# +proc ::iwidgets::combobox {pathName args} { + uplevel ::iwidgets::Combobox $pathName $args +} + +# ------------------------------------------------------------------ +# CONSTRUCTOR +# ------------------------------------------------------------------ +body iwidgets::Combobox::constructor {args} { + set _listShowing($this) 0 + + # combobox is different as all components are created + # after determining what the dropdown style is... + + # configure args + eval itk_initialize $args + + # create components that are dependent on options + # (Scrolledlistbox, arrow button) and pack them. + if {$count == 0} { + image create bitmap downarrow -data { + #define down_width 16 + #define down_height 16 + static unsigned char down_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x7f, 0xf8, 0x3f, + 0xf0, 0x1f, 0xe0, 0x0f, 0xc0, 0x07, 0x80, 0x03, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + } + image create bitmap uparrow -data { + #define up_width 16 + #define up_height 16 + static unsigned char up_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, + 0xfc, 0x1f, 0xfe, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + } + } + incr count + _doLayout +} + +# ------------------------------------------------------------------ +# DESTRUCTOR +# ------------------------------------------------------------------ +body iwidgets::Combobox::destructor {} { + # catch any repacking that may be waiting for idle time + if {$_repacking != ""} { + after cancel $_repacking + } + incr count -1 + if {$count == 0} { + image delete uparrow + image delete downarrow + } +} + +# ================================================================ +# OPTIONS +# ================================================================ + +# -------------------------------------------------------------------- +# OPTION: -arrowrelief +# +# Relief style used on the arrow button. +# -------------------------------------------------------------------- +configbody iwidgets::Combobox::arrowrelief {} + +# -------------------------------------------------------------------- +# OPTION: -completion +# +# Relief style used on the arrow button. +# -------------------------------------------------------------------- +configbody iwidgets::Combobox::completion { + switch -- $itk_option(-completion) { + 0 - no - false - off { } + 1 - yes - true - on { } + default { + error "bad completion option \"$itk_option(-completion)\":\ + should be boolean" + } + } +} + +# -------------------------------------------------------------------- +# OPTION: -dropdown +# +# Boolean which determines the Combobox style: dropdown or simple. +# Because the two style's lists reside in different toplevel widgets +# this is more complicated than it should be. +# -------------------------------------------------------------------- +configbody iwidgets::Combobox::dropdown { + switch -- $itk_option(-dropdown) { + 1 - yes - true - on { + if {[winfo exists $itk_interior.list]} { + set vals [$itk_component(list) get 0 end] + destroy $itk_component(list) + _doLayout + if [llength $vals] { + eval insert list end $vals + } + } + } + 0 - no - false - off { + if {[winfo exists $itk_interior.popup.list]} { + set vals [$itk_component(list) get 0 end] + catch {destroy $itk_component(arrowBtn)} + destroy $itk_component(popup) ;# this deletes the list too + _doLayout + if [llength $vals] { + eval insert list end $vals + } + } + } + default { + error "bad dropdown option \"$itk_option(-dropdown)\":\ + should be boolean" + } + } +} + +# -------------------------------------------------------------------- +# OPTION: -editable +# +# Boolean which allows/disallows user input to the entry field area. +# -------------------------------------------------------------------- +configbody iwidgets::Combobox::editable { + switch -- $itk_option(-editable) { + 1 - true - yes - on { + switch -- $itk_option(-state) { + normal { + $itk_component(entry) configure -state normal + } + } + } + 0 - false - no - off { + $itk_component(entry) configure -state disabled + } + default { + error "bad editable option \"$itk_option(-editable)\":\ + should be boolean" + } + } +} + +# -------------------------------------------------------------------- +# OPTION: -grab +# +# grab-state of megawidget +# -------------------------------------------------------------------- +configbody iwidgets::Combobox::grab { + switch -- $itk_option(-grab) { + local { } + global { } + default { + error "bad grab value \"$itk_option(-grab)\":\ + must be global or local" + } + } +} + +# -------------------------------------------------------------------- +# OPTION: -listheight +# +# Listbox height in pixels. (Need to integrate the scrolledlistbox +# -visibleitems option here - at least for simple listbox.) +# -------------------------------------------------------------------- +configbody iwidgets::Combobox::listheight {} + +# -------------------------------------------------------------------- +# OPTION: -margin +# +# Spacer between the entry field and arrow button of dropdown style +# Comboboxes. +# -------------------------------------------------------------------- +configbody iwidgets::Combobox::margin { + grid columnconfigure $itk_interior 0 -minsize $itk_option(-margin) +} + +# -------------------------------------------------------------------- +# OPTION: -popupcursor +# +# Set the cursor for the popup list. +# -------------------------------------------------------------------- +configbody iwidgets::Combobox::popupcursor {} + +# -------------------------------------------------------------------- +# OPTION: -selectioncommand +# +# Defines the proc to be called when an item is selected in the list. +# -------------------------------------------------------------------- +configbody iwidgets::Combobox::selectioncommand {} + +# -------------------------------------------------------------------- +# OPTION: -state +# +# overall state of megawidget +# -------------------------------------------------------------------- +configbody iwidgets::Combobox::state { + switch -- $itk_option(-state) { + disabled { + $itk_component(entry) configure -state disabled + } + normal { + switch -- $itk_option(-editable) { + 1 - true - yes - on { + $itk_component(entry) configure -state normal + } + 0 - false - no - off { + $itk_component(entry) configure -state disabled + } + } + } + default { + error "bad state value \"$itk_option(-state)\":\ + must be normal or disabled" + } + } + if {[info exists itk_component(arrowBtn)]} { + $itk_component(arrowBtn) configure -state $itk_option(-state) + } +} + +# -------------------------------------------------------------------- +# OPTION: -unique +# +# Boolean which disallows/allows adding duplicate items to the listbox. +# -------------------------------------------------------------------- +configbody iwidgets::Combobox::unique { + # boolean error check + switch -- $itk_option(-unique) { + 1 - true - yes - on { } + 0 - false - no - off { } + default { + error "bad unique value \"$itk_option(-unique)\":\ + should be boolean" + } + } +} + +# ================================================================= +# METHODS +# ================================================================= + +# ------------------------------------------------------ +# PUBLIC METHOD: clear ?component? +# +# Remove all elements from the listbox, all contents +# from the entry component, or both (if all). +# +# ------------------------------------------------------ +body iwidgets::Combobox::clear {{component all}} { + switch -- $component { + entry { + iwidgets::Entryfield::clear + } + list { + delete list 0 end + } + all { + delete list 0 end + iwidgets::Entryfield::clear + } + default { + error "bad Combobox component \"$component\":\ + must be entry, list, or all." + } + } + return +} + +# ------------------------------------------------------ +# PUBLIC METHOD: curselection +# +# Return the current selection index. +# +# ------------------------------------------------------ +body iwidgets::Combobox::curselection {} { + return [$itk_component(list) curselection] +} + +# ------------------------------------------------------ +# PUBLIC METHOD: delete component first ?last? +# +# Delete an item or items from the listbox OR delete +# text from the entry field. First argument determines +# which component deletion occurs in - valid values are +# entry or list. +# +# ------------------------------------------------------ +body iwidgets::Combobox::delete {component first {last {}}} { + switch -- $component { + entry { + iwidgets::Entryfield::delete $first $last + } + list { + _deleteList $first $last + } + default { + error "bad Combobox component \"$component\":\ + must be entry or list." + } + } +} + +# ------------------------------------------------------ +# PUBLIC METHOD: get ?index? +# +# +# Retrieve entry contents if no args OR use args as list +# index and retrieve list item at index . +# +# ------------------------------------------------------ +body iwidgets::Combobox::get {{index {}}} { + # no args means to get the current text in the entry field area + if {$index == {}} { + iwidgets::Entryfield::get + } else { + eval $itk_component(list) get $index + } +} + +# ------------------------------------------------------ +# PUBLIC METHOD: getcurselection +# +# Return currently selected item in the listbox. Shortcut +# version of get curselection command combination. +# +# ------------------------------------------------------ +body iwidgets::Combobox::getcurselection {} { + return [$itk_component(list) getcurselection] +} + +# ------------------------------------------------------------------ +# PUBLIC METHOD: ivoke +# +# Pops up or down a dropdown combobox. +# +# ------------------------------------------------------------------ +body iwidgets::Combobox::invoke {} { + if {$itk_option(-dropdown)} { + return [_toggleList] + } + return +} + +# ------------------------------------------------------------ +# PUBLIC METHOD: insert comonent index string ?string ...? +# +# Insert an item into the listbox OR text into the entry area. +# Valid component names are entry or list. +# +# ------------------------------------------------------------ +body iwidgets::Combobox::insert {component index args} { + set nargs [llength $args] + + if {$nargs == 0} { + error "no value given for parameter \"string\" in function\ + \"Combobox::insert\"" + } + + switch -- $component { + entry { + if { $nargs > 1} { + error "called function \"Combobox::insert entry\"\ + with too many arguments" + } else { + if {$itk_option(-state) == "normal"} { + eval iwidgets::Entryfield::insert $index $args + [code $this _lookup ""] + } + } + } + list { + if {$itk_option(-state) == "normal"} { + eval $itk_component(list) insert $index $args + } + } + default { + error "bad Combobox component \"$component\": must\ + be entry or list." + } + } +} + +# ------------------------------------------------------ +# PUBLIC METHOD: justify direction +# +# Wrapper for justifying the listbox items in one of +# 4 directions: top, bottom, left, or right. +# +# ------------------------------------------------------ +body iwidgets::Combobox::justify {direction} { + return [$itk_component(list) justify $direction] +} + +# ------------------------------------------------------------------ +# PUBLIC METHOD: see index +# +# Adjusts the view such that the element given by index is visible. +# ------------------------------------------------------------------ +body iwidgets::Combobox::see {index} { + return [$itk_component(list) see $index] +} + +# ------------------------------------------------------------------ +# PUBLIC METHOD: selection option first ?last? +# +# Adjusts the selection within the listbox and changes the contents +# of the entry component to be the value of the selected list item. +# ------------------------------------------------------------------ +body iwidgets::Combobox::selection {option first {last {}}} { + # thin wrap + if {$option == "set"} { + $itk_component(list) selection clear 0 end + $itk_component(list) selection set $first + set rtn "" + } else { + set rtn [eval $itk_component(list) selection $option $first $last] + } + set _currItem $first + + # combobox additions + set theText [getcurselection] + if {$theText != [$itk_component(entry) get]} { + clear entry + if {$theText != ""} { + insert entry 0 $theText + } + } + return $rtn +} + +# ------------------------------------------------------------------ +# PUBLIC METHOD: size +# +# Returns a decimal string indicating the total number of elements +# in the listbox. +# ------------------------------------------------------------------ +body iwidgets::Combobox::size {} { + return [$itk_component(list) size] +} + +# ------------------------------------------------------ +# PUBLIC METHOD: sort ?mode? +# +# Sort the current list in either "ascending" or "descending" order. +# +# jss: how should i handle selected items? +# +# ------------------------------------------------------ +body iwidgets::Combobox::sort {{mode ascending}} { + $itk_component(list) sort $mode + # return [$itk_component(list) sort $mode] +} + + +# ------------------------------------------------------------------ +# PUBLIC METHOD: xview ?arg arg ...? +# +# Change or query the vertical position of the text in the list box. +# ------------------------------------------------------------------ +body iwidgets::Combobox::xview {args} { + return [eval $itk_component(list) xview $args] +} + +# ------------------------------------------------------------------ +# PUBLIC METHOD: yview ?arg arg ...? +# +# Change or query the horizontal position of the text in the list box. +# ------------------------------------------------------------------ +body iwidgets::Combobox::yview {args} { + return [eval $itk_component(list) yview $args] +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _addToList +# +# Add the current item in the entry to the listbox. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_addToList {} { + set input [get] + if {$input != ""} { + if {$itk_option(-unique)} { + # if item is already in list, select it and exit + set item [lsearch -exact [$itk_component(list) get 0 end] $input] + if {$item != -1} { + selection clear 0 end + if {$item != {}} { + selection set $item $item + set _currItem $item + } + return + } + } + # add the item to end of list + selection clear 0 end + insert list end $input + selection set end end + } +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _createComponents +# +# Create deferred combobox components and add bindings. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_createComponents {} { + if {$itk_option(-dropdown)} { + # --- build a dropdown combobox --- + + # make the arrow childsite be on the right hand side + configure -childsitepos e -command [code $this _addToList] + + # arrow button to popup the list + itk_component add arrowBtn { + button $itk_interior.arrowBtn -borderwidth 2 \ + -width 15 -height 15 -image downarrow \ + -command [code $this _toggleList] -state $itk_option(-state) + } { + keep -background -borderwidth -cursor -state \ + -highlightcolor -highlightthickness + rename -relief -arrowrelief arrowRelief Relief + rename -highlightbackground -background background Background + } + + # popup list container + itk_component add popup { + toplevel $itk_interior.popup + } { + keep -background -cursor + } + wm withdraw $itk_interior.popup + + # the listbox + itk_component add list { + iwidgets::Scrolledlistbox $itk_interior.popup.list -exportselection no \ + -vscrollmode dynamic -hscrollmode dynamic -selectmode browse + } { + keep -background -borderwidth -cursor -foreground \ + -highlightcolor -highlightthickness \ + -hscrollmode -selectbackground \ + -selectborderwidth -selectforeground -textbackground \ + -textfont -vscrollmode + rename -height -listheight listHeight Height + rename -cursor -popupcursor popupCursor Cursor + } + # mode specific bindings + _dropdownBindings + + # Ugly hack to avoid tk buglet revealed in _dropdownBtnRelease where + # relief is used but not set in scrollbar.tcl. + global tkPriv + set tkPriv(relief) raise + + } else { + # --- build a simple combobox --- + configure -childsitepos s + itk_component add list { + iwidgets::Scrolledlistbox $itk_interior.list -exportselection no \ + -vscrollmode dynamic -hscrollmode dynamic + } { + keep -background -borderwidth -cursor -foreground \ + -highlightcolor -highlightthickness \ + -hscrollmode -selectbackground \ + -selectborderwidth -selectforeground -textbackground \ + -textfont -visibleitems -vscrollmode + rename -height -listheight listHeight Height + } + # add mode specific bindings + _simpleBindings + } + + # popup cursor applies only to the list within the combobox + configure -popupcursor $itk_option(-popupcursor) + + # add mode independent bindings + _commonBindings +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _deleteList first ?last? +# +# Delete an item or items from the listbox. Called via +# "delete list args". +# +# ------------------------------------------------------ +body iwidgets::Combobox::_deleteList {first {last {}}} { + + if {$last == {}} { + set last $first + } + $itk_component(list) delete $first $last + + # remove the item if it is no longer in the list + set text [$this get] + if {$text != ""} { + set index [lsearch -exact [$itk_component(list) get 0 end] $text ] + if {$index == -1} { + clear entry + } + } + return +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _deleteText first ?last? +# +# Renamed Entryfield delete method. Called via "delete entry args". +# +# ------------------------------------------------------ +body iwidgets::Combobox::_deleteText {first {last {}}} { + $itk_component(entry) configure -state normal + set rtrn [delete $first $last] + switch -- $itk_option(-editable) { + 0 - false - no - off { + $itk_component(entry) configure -state disabled + } + } + return $rtrn +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _doLayout ?when? +# +# Call methods to create and pack the Combobox components. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_doLayout {{when later}} { + _createComponents + _packComponents $when +} + + +# ------------------------------------------------------ +# PROTECTED METHOD: _drawArrow +# +# Draw the arrow button. Determines packing according to +# -labelpos. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_drawArrow {} { + set flip false + set relief "" + set fg [cget -foreground] + if {$_isPosted} { + set flip true + set relief "-relief sunken" + } else { + set relief "-relief $itk_option(-arrowrelief)" + } + + if {$flip} { + # + # draw up arrow + # + eval $itk_component(arrowBtn) configure -image uparrow $relief + } else { + # + # draw down arrow + # + eval $itk_component(arrowBtn) configure -image downarrow $relief + } +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _dropdownBtnRelease window x y +# +# Event handler for button releases while a dropdown list +# is posted. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_dropdownBtnRelease {{window {}} {x 1} {y 1}} { + + # if it's a scrollbar then ignore the release + if {($window == [$itk_component(list) component vertsb]) || + ($window == [$itk_component(list) component horizsb])} { + return + } + + # 1st release allows list to stay up unless we are in listbox + if {$_ignoreRelease} { + _ignoreNextBtnRelease false + return + } + + # should I use just the listbox or also include the scrollbars + if { ($x >= 0) && ($x < [winfo width [_slbListbox]]) + && ($y >= 0) && ($y < [winfo height [_slbListbox]])} { + _stateSelect + } + + _unpostList +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _ignoreNextBtnRelease ignore +# +# Set private variable _ignoreRelease. If this variable +# is true then the next button release will not remove +# a dropdown list. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_ignoreNextBtnRelease {ignore} { + set _ignoreRelease $ignore +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _next +# +# Select the next item in the list. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_next {} { + if {[size] <= 1} { + return + } + set i [curselection] + if {($i == {}) || ($i == [expr [size]-1]) } { + set i 0 + } else { + incr i + } + selection clear 0 end + selection set $i $i + see $i + set _currItem $i +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _packComponents ?when? +# +# Pack the components of the combobox and add bindings. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_packComponents {{when later}} { + if {$when == "later"} { + if {$_repacking == ""} { + set _repacking [after idle [code $this _packComponents now]] + return + } + } elseif {$when != "now"} { + error "bad option \"$when\": should be now or later" + } + + if {$itk_option(-dropdown)} { + grid configure $itk_component(list) -row 1 -column 0 -sticky news + _resizeArrow + grid config $itk_component(arrowBtn) -row 0 -column 1 -sticky nsew + } else { + # size and pack list hack + grid configure $itk_component(entry) -row 0 -column 0 -sticky ew + grid configure $itk_component(efchildsite) -row 1 -column 0 -sticky nsew + grid configure $itk_component(list) -row 0 -column 0 -sticky nsew + + grid rowconfigure $itk_component(efchildsite) 1 -weight 1 + grid columnconfigure $itk_component(efchildsite) 0 -weight 1 + } + set _repacking "" +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _positionList +# +# Determine the position (geometry) for the popped up list +# and map it to the screen. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_positionList {} { + + set x [winfo rootx $itk_component(entry) ] + set y [expr [winfo rooty $itk_component(entry) ] + \ + [winfo height $itk_component(entry) ]] + set w [winfo width $itk_component(entry) ] + set h [winfo height [_slbListbox] ] + set sh [winfo screenheight .] + + if {([expr $y+$h] > $sh) && ($y > [expr $sh/2])} { + set y [expr [winfo rooty $itk_component(entry) ] - $h] + } + + $itk_component(list) configure -width $w + wm overrideredirect $itk_component(popup) 0 + wm geometry $itk_component(popup) +$x+$y + wm overrideredirect $itk_component(popup) 1 +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _postList +# +# Pop up the list in a dropdown style Combobox. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_postList {} { + if {[$itk_component(list) size] == ""} { + return + } + + set _isPosted true + _positionList + + # map window and do a grab + wm deiconify $itk_component(popup) + _listShowing -wait + if {$itk_option(-grab) == "global"} { + grab -global $itk_component(popup) + } else { + grab $itk_component(popup) + } + raise $itk_component(popup) + focus $itk_component(popup) + _drawArrow +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _previous +# +# Select the previous item in the list. Wraps at front +# and end of list. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_previous {} { + if {[size] <= 1} { + return + } + set i [curselection] + if {$i == "" || $i == 0} { + set i [expr [size] - 1] + } else { + incr i -1 + } + selection clear 0 end + selection set $i $i + see $i + set _currItem $i +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _resizeArrow +# +# Recalculate the arrow button size and then redraw it. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_resizeArrow {} { + set bw [expr [$itk_component(arrowBtn) cget -borderwidth]+ \ + [$itk_component(arrowBtn) cget -highlightthickness]] + set newHeight [expr [winfo reqheight $itk_component(entry) ]-(2*$bw) - 2] + $itk_component(arrowBtn) configure -width $newHeight -height $newHeight + _drawArrow +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _selectCmd +# +# Called when list item is selected to insert new text +# in entry, and call user -command callback if defined. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_selectCmd {} { + $itk_component(entry) configure -state normal + + set _currItem [$itk_component(list) curselection] + set item [$itk_component(list) getcurselection] + clear entry + $itk_component(entry) insert 0 $item + switch -- $itk_option(-editable) { + 0 - false - no - off { + $itk_component(entry) configure -state disabled + } + } + + # execute user command + if {$itk_option(-selectioncommand) != ""} { + uplevel #0 $itk_option(-selectioncommand) + } +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _toggleList +# +# Post or unpost the dropdown listbox (toggle). +# +# ------------------------------------------------------ +body iwidgets::Combobox::_toggleList {} { + if {[winfo ismapped $itk_component(popup)] } { + _unpostList + } else { + _postList + } +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _unpostList +# +# Unmap the listbox (pop it down). +# +# ------------------------------------------------------ +body iwidgets::Combobox::_unpostList {} { + # Determine if event occured in the scrolledlistbox and, if it did, + # don't unpost it. (A selection in the list unposts it correctly and + # in the scrollbar we don't want to unpost it.) + set x [winfo x $itk_component(list)] + set y [winfo y $itk_component(list)] + set w [winfo width $itk_component(list)] + set h [winfo height $itk_component(list)] + + wm withdraw $itk_component(popup) + grab release $itk_component(popup) + + set _isPosted false + + $itk_component(list) selection clear 0 end + if {$_currItem != {}} { + $itk_component(list) selection set $_currItem $_currItem + $itk_component(list) activate $_currItem + } + + switch -- $itk_option(-editable) { + 1 - true - yes - on { + $itk_component(entry) configure -state normal + } + 0 - false - no - off { + $itk_component(entry) configure -state disabled + } + } + + _drawArrow +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _commonBindings +# +# Bindings that are used by both simple and dropdown +# style Comboboxes. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_commonBindings {} { + bind $itk_component(entry) <KeyPress-BackSpace> [code $this _bs] + bind $itk_component(entry) <KeyRelease> [code $this _lookup %K] + bind $itk_component(entry) <Down> [code $this _next] + bind $itk_component(entry) <Up> [code $this _previous] + bind $itk_component(entry) <Control-n> [code $this _next] + bind $itk_component(entry) <Control-p> [code $this _previous] + bind [_slbListbox] <Control-n> [code $this _next] + bind [_slbListbox] <Control-p> [code $this _previous] +} + + +# ------------------------------------------------------ +# PROTECTED METHOD: _dropdownBindings +# +# Bindings used only by the dropdown type Combobox. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_dropdownBindings {} { + bind $itk_component(popup) <Escape> [code $this _unpostList] + bind $itk_component(popup) <space> \ + "[code $this _stateSelect]; [code $this _unpostList]" + bind $itk_component(popup) <Return> \ + "[code $this _stateSelect]; [code $this _unpostList]" + bind $itk_component(popup) <ButtonRelease-1> \ + [code $this _dropdownBtnRelease %W %x %y] + + bind $itk_component(list) <Map> \ + [code $this _listShowing 1] + bind $itk_component(list) <Unmap> \ + [code $this _listShowing 0] + + # once in the listbox, we drop on the next release (unless in scrollbar) + bind [_slbListbox] <Enter> \ + [code $this _ignoreNextBtnRelease false] + + bind $itk_component(arrowBtn) <3> [code $this _next] + bind $itk_component(arrowBtn) <Shift-3> [code $this _previous] + bind $itk_component(arrowBtn) <Down> [code $this _next] + bind $itk_component(arrowBtn) <Up> [code $this _previous] + bind $itk_component(arrowBtn) <Control-n> [code $this _next] + bind $itk_component(arrowBtn) <Control-p> [code $this _previous] + bind $itk_component(arrowBtn) <Shift-Down> [code $this _toggleList] + bind $itk_component(arrowBtn) <Shift-Up> [code $this _toggleList] + bind $itk_component(arrowBtn) <Return> [code $this _toggleList] + bind $itk_component(arrowBtn) <space> [code $this _toggleList] + + bind $itk_component(entry) <Configure> [code $this _resizeArrow] + bind $itk_component(entry) <Shift-Down> [code $this _toggleList] + bind $itk_component(entry) <Shift-Up> [code $this _toggleList] +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _simpleBindings +# +# Bindings used only by the simple type Comboboxes. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_simpleBindings {} { + bind [_slbListbox] <ButtonRelease-1> [code $this _stateSelect] + # "[code $this _stateselect]; [code $this _selectCmd]" + + + bind [_slbListbox] <space> [code $this _stateSelect] + bind [_slbListbox] <Return> [code $this _stateSelect] + bind $itk_component(entry) <Escape> "" + bind $itk_component(entry) <Shift-Down> "" + bind $itk_component(entry) <Shift-Up> "" + bind $itk_component(entry) <Configure> "" +} + +# ------------------------------------------------------ +# PROTECTED METHOD: _listShowing ?val? +# +# Used instead of "tkwait visibility" to make sure that +# the dropdown list is visible. Whenever the list gets +# mapped or unmapped, this method is called to keep +# track of it. When it is called with the value "-wait", +# it waits for the list to be mapped. +# ------------------------------------------------------ +body iwidgets::Combobox::_listShowing {{val ""}} { + if {$val == ""} { + return $_listShowing($this) + } elseif {$val == "-wait"} { + while {!$_listShowing($this)} { + tkwait variable [scope _listShowing($this)] + } + return + } + set _listShowing($this) $val +} + +# ------------------------------------------------------ +# PRIVATE METHOD: _slbListbox +# +# Access the tk listbox window out of the scrolledlistbox. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_slbListbox {} { + return [$itk_component(list) component listbox] +} + +# ------------------------------------------------------ +# PRIVATE METHOD: _stateSelect +# +# only allows a B1 release in the listbox to have an effect if -state is +# normal. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_stateSelect {} { + switch -- $itk_option(-state) { + normal { + [code $this _selectCmd] + } + } +} + +# ------------------------------------------------------ +# PRIVATE METHOD: _bs +# +# A part of the auto-completion code, this function sets a flag when the +# Backspace key is hit and there is a selection in the entry field. +# Note that it's probably buggy to assume that a selection being present +# means that that selection came from auto-completion. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_bs {} { + # + # exit if completion is turned off + # + switch -- $itk_option(-completion) { + 0 - no - false - off { + return + } + } + # + # critical section flag. it ain't perfect, but for most usage it'll + # keep us from being in this code "twice" at the same time + # (auto-repeated keystrokes are a pain!) + # + if {$_inbs} { + return + } else { + set _inbs 1 + } + + # + # set the _doit flag if there is a selection set in the entry field + # + set _doit 0 + if [$itk_component(entry) selection present] { + set _doit 1 + } + + # + # clear the semaphore and return + # + set _inbs 0 +} + +# ------------------------------------------------------ +# PRIVATE METHOD: _lookup +# +# handles auto-completion of text typed (or insert'd) into the entry field. +# +# ------------------------------------------------------ +body iwidgets::Combobox::_lookup {key} { + # + # exit if completion is turned off + # + switch -- $itk_option(-completion) { + 0 - no - false - off { + return + } + } + + # + # critical section flag. it ain't perfect, but for most usage it'll + # keep us from being in this code "twice" at the same time + # (auto-repeated keystrokes are a pain!) + # + if {$_inlookup} { + return + } else { + set _inlookup 1 + } + + # + # if state of megawidget is disabled, or the entry is not editable, + # clear the semaphore and exit + # + if {$itk_option(-state) == "disabled" \ + || [lsearch {on 1 true yes} $itk_option(-editable)] == -1} { + set _inlookup 0 + return + } + + # + # okay, *now* we can get to work + # the _bs function is called on keyPRESS of BackSpace, and will set + # the _doit flag if there's a selection set in the entryfield. If + # there is, we're assuming that it's generated by completion itself + # (this is probably a Bad Assumption), so we'll want to whack the + # selected text, as well as the character immediately preceding the + # insertion cursor. + # + if {$key == "BackSpace"} { + if {$_doit} { + set first [expr [$itk_component(entry) index insert] -1] + $itk_component(entry) delete $first end + $itk_component(entry) icursor $first + } + } + + # + # get the text left in the entry field, and its length. if + # zero-length, clear the selection in the listbox, clear the + # semaphore, and boogie. + # + set text [get] + set len [string length $text] + if {$len == 0} { + $itk_component(list) selection clear 0 end + set _inlookup 0 + return + } + + # + # okay, so we have to do a lookup. find the first match in the + # listbox to the text we've got in the entry field (glob). + # if one exists, clear the current listbox selection, and set it to + # the one we just found, making that one visible in the listbox. + # then, pick off the text from the listbox entry that hadn't yet been + # entered into the entry field. we need to tack that text onto the + # end of the entry field, select it, and then set the insertion cursor + # back to just before the point where we just added that text. + # if one didn't exist, then just clear the listbox selection + # + set item [lsearch [$itk_component(list) get 0 end] "$text*" ] + if {$item != -1} { + $itk_component(list) selection clear 0 end + $itk_component(list) selection set $item $item + see $item + set remainder [string range [$itk_component(list) get $item] \ + $len end] + $itk_component(entry) insert end $remainder + $itk_component(entry) selection range $len end + $itk_component(entry) icursor $len + } else { + $itk_component(list) selection clear 0 end + } + # + # clear the semaphore and return + # + set _inlookup 0 + return +} |