# # 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 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. # - determine clean way to have a state for listbox/canvas. # - add completion functionality when typing in the entry. # - add grab option: global, local, none. # # ---------------------------------------------------------------------- # AUTHOR: John S. Sigler EMAIL: jsigler@spd.dsccc.com # sigler@onramp.net # @(#) $Id: combobox.itk,v 1.3 2006/09/11 20:35:54 irby Exp $ # ---------------------------------------------------------------------- # Copyright (c) 1995 John S. Sigler # ====================================================================== # 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 w 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 -autoclear autoClear AutoClear true itk_option define -arrowrelief arrowRelief Relief raised itk_option define -dropdown dropdown Dropdown true itk_option define -editable editable Editable true itk_option define -fliparrow flipArrow FlipArrow false itk_option define -items items Items "" itk_option define -labelpos labelPos Position w 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 -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 {{mode normal}} protected method _dropdownBtnRelease {{window {}} {x 1} {y 1}} protected method _ignoreNextBtnRelease {ignore} protected method _insertText {index string} protected method _next {} protected method _packArrow {labelpos} 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 _slbListbox {} private variable _currItem {}; ;# current selected item. private variable _ignoreRelease true ;# next button release ignored. private variable _isPosted false; ;# is the dropdown popped up. private variable _repacking {} ;# non-null => _packComponents pending. private common _listShowing } # # 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. _doLayout } # ------------------------------------------------------------------ # DESTRUCTOR # ------------------------------------------------------------------ body iwidgets::Combobox::destructor {} { # catch any repacking that may be waiting for idle time if {$_repacking != ""} { after cancel $_repacking } } # ================================================================ # OPTIONS # ================================================================ # -------------------------------------------------------------------- # OPTION: -autoclear # # Specifies wheather or not to clear the entry field as items are # added to the list. The default is true. # -------------------------------------------------------------------- configbody iwidgets::Combobox::autoclear {} # -------------------------------------------------------------------- # OPTION: -arrowrelief # # Relief style used on the arrow button. # -------------------------------------------------------------------- configbody iwidgets::Combobox::arrowrelief {} # -------------------------------------------------------------------- # 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 { if {$itk_option(-dropdown)} { if {[winfo exists $itk_interior.list]} { destroy $itk_component(list) _doLayout } } else { if {[winfo exists $itk_interior.popup.list]} { destroy $itk_component(margin) destroy $itk_component(arrowBtn) destroy $itk_component(popup) ;# this deletes the list too _doLayout } } } # -------------------------------------------------------------------- # OPTION: -editable # # Boolean which allows/disallows user input to the entry field area. # -------------------------------------------------------------------- configbody iwidgets::Combobox::editable { if {$itk_option(-editable)} { configure -state normal -command [code $this _addToList] bind $itk_component(entry) <1> "" } else { configure -state disabled bind $itk_component(entry) <1> [code $this _toggleList] } } # -------------------------------------------------------------------- # OPTION: -fliparrow # # Boolean which causes dropdown comboboxes to have their arrow point # up when the listbox is displayed. # -------------------------------------------------------------------- configbody iwidgets::Combobox::fliparrow { # boolean error check if {$itk_option(-fliparrow)} { } } # -------------------------------------------------------------------- # OPTION: -items # # Set/get the current list of items in the listbox. # -------------------------------------------------------------------- configbody iwidgets::Combobox::items { # clear out what was in entry field before if {$itk_option(-autoclear)} { delete entry 0 end } if {[winfo exists $itk_interior.popup.list]} { _drawArrow normal } } # -------------------------------------------------------------------- # OPTION: -labelpos # # Labelpos. Overrides the labelpos of the labeledwidget. This doesn't # work as positionalLabel seems to get called after this routine is called! # -------------------------------------------------------------------- configbody iwidgets::Combobox::labelpos { iwidgets::Labeledwidget::_positionLabel switch -- $itk_option(-labelpos) { e - w { pack configure $itk_component(label) -anchor n } } } # -------------------------------------------------------------------- # 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 {} # -------------------------------------------------------------------- # 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: -unique # # Boolean which disallows/allows adding duplicate items to the listbox. # -------------------------------------------------------------------- configbody iwidgets::Combobox::unique { # boolean error check if {$itk_option(-unique)} { } } # ================================================================= # 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} { if {$args == {}} { error "no value given for parameter \"string\" in function\ \"Combobox::insert\"" } switch -- $component { entry { if { [llength $args] > 1} { error "called function \"Combobox::insert entry\" with too\ many arguments" } else { eval iwidgets::Entryfield::insert $index $args } } list { configure -items [eval linsert {$itk_option(-items)} $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 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}} { 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_option(-items) $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 and clear the text area insert list end $input if {$itk_option(-autoclear)} { delete entry 0 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 and margin childsite's be on the right hand side configure -childsitepos e # add spacer between arrow and the entry component itk_component add margin { frame $itk_interior.margin -width $itk_option(-margin) } { rename -width -margin margin Margin } # arrow button to popup the list itk_component add arrowBtn { canvas $itk_interior.arrowBtn -borderwidth 2 \ -width 16 -height 16 } { keep -background -borderwidth -cursor \ -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 wm overrideredirect $itk_interior.popup 1 # the listbox itk_component add list { iwidgets::Scrolledlistbox $itk_interior.popup.list -exportselection no \ -vscrollmode dynamic -hscrollmode dynamic } { keep -background -borderwidth -cursor -foreground \ -highlightcolor -highlightthickness \ -hscrollmode -items -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 -items -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 } configure -items [eval lreplace {$itk_option(-items)} $first $last] # remove the item if it is no longer in the list set text [$this get] if {$text != ""} { set index [lsearch -exact $itk_option(-items) $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] if {$itk_option(-editable)} { } else { $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 ?mode? # # Draw the arrow button. Determines packing according to # -labelpos. # # ------------------------------------------------------ body iwidgets::Combobox::_drawArrow {{mode normal}} { set flip false switch -- $mode { normal { set fg [cget -foreground] $itk_component(arrowBtn) configure -relief $itk_option(-arrowrelief) bind $itk_component(arrowBtn) <1> [code $this _toggleList] if {$_isPosted} { if {$itk_option(-fliparrow)} { set flip true } } } disabled { set fg grey65 $itk_component(arrowBtn) configure -relief $itk_option(-arrowrelief) bind $itk_component(arrowBtn) <1> "" } depressed { set fg [cget -foreground] $itk_component(arrowBtn) configure -relief sunken # update } } # undraw the old arrow polygon $itk_component(arrowBtn) delete arrow # draw new arrow set bw [expr ([$itk_component(arrowBtn) cget -borderwidth] + \ [$itk_component(arrowBtn) cget -highlightthickness]) / 2] set h [expr [$itk_component(arrowBtn) cget -height] + (2*$bw)] set w [expr [$itk_component(arrowBtn) cget -width] + (2*$bw)] if {$flip} { $itk_component(arrowBtn) create polygon \ [expr .25*$w+$bw] [expr .75*$h+$bw] \ [expr .75*$w+$bw] [expr .75*$h+$bw] \ [expr .5*$w+$bw] [expr .25*$h+$bw-1] -fill $fg -tag arrow } else { $itk_component(arrowBtn) create polygon \ [expr .25*$w+$bw] [expr .25*$h+$bw] \ [expr .75*$w+$bw] [expr .25*$h+$bw] \ [expr .5*$w+$bw] [expr .75*$h+$bw] \ -fill $fg -tag arrow } } # ------------------------------------------------------ # 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]])} { _selectCmd } _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 {[$this size] <= 1} { return } set i [$this curselection] if {($i == {}) || ($i == [expr [$this size]-1]) } { set i 0 } else { incr i } $this selection clear 0 end $this selection set $i $i $this see $i set _currItem $i } # ------------------------------------------------------ # PROTECTED METHOD: _packArrow labelpos # # Pack the arrowbutton according to the label position. # # ------------------------------------------------------ body iwidgets::Combobox::_packArrow {labelpos} { pack $itk_component(arrowBtn) -side right -anchor center } # ------------------------------------------------------ # 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)} { pack $itk_component(margin) -side left pack configure $itk_component(list) -expand yes -fill both _resizeArrow pack $itk_component(arrowBtn) -side right } else { # size and pack list hack set w [winfo reqwidth $itk_component(hull)] $itk_component(list) configure -width $w pack configure $itk_component(entry) -fill x -side top -expand no pack configure $itk_component(efchildsite) -side top \ -after $itk_component(entry) -fill both -expand yes pack configure $itk_component(list) -side top -fill both -expand yes } 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 geometry $itk_component(popup) +$x+$y } # ------------------------------------------------------ # 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 _drawArrow depressed ;# sad button _positionList # map window and do a grab wm deiconify $itk_component(popup) _listShowing -wait grab -global $itk_component(popup) raise $itk_component(popup) focus $itk_component(popup) _drawArrow normal set _ignoreRelease true } # ------------------------------------------------------ # PROTECTED METHOD: _previous # # Select the previous item in the list. Wraps at front # and end of list. # # ------------------------------------------------------ body iwidgets::Combobox::_previous {} { if {[$this size] <= 1} { return } set i [$this curselection] if {$i == "" || $i == 0} { set i [expr [$this size] - 1] } else { incr i -1 } $this selection clear 0 end $this selection set $i $i $this 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)] $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 if {$itk_option(-editable)} { } else { $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 _drawArrow normal $itk_component(list) selection clear 0 end if {$_currItem != {}} { $itk_component(list) selection set $_currItem $_currItem $itk_component(list) activate $_currItem } if {$itk_option(-editable)} { $itk_component(entry) configure -state normal } else { $itk_component(entry) configure -state disabled } } # ------------------------------------------------------ # PROTECTED METHOD: _commonBindings # # Bindings that are used by both simple and dropdown # style Comboboxes. # # ------------------------------------------------------ body iwidgets::Combobox::_commonBindings {} { bind $itk_component(entry) [code $this _next] bind $itk_component(entry) [code $this _previous] bind $itk_component(entry) [code $this _next] bind $itk_component(entry) [code $this _previous] bind [_slbListbox] [code $this _next] bind [_slbListbox] [code $this _previous] } # ------------------------------------------------------ # PROTECTED METHOD: _dropdownBindings # # Bindings used only by the dropdown type Combobox. # # ------------------------------------------------------ body iwidgets::Combobox::_dropdownBindings {} { bind $itk_component(popup) [code $this _unpostList] bind $itk_component(popup) \ "[code $this _selectCmd]; [code $this _unpostList]" bind $itk_component(popup) \ "[code $this _selectCmd]; [code $this _unpostList]" bind $itk_component(popup) \ [code $this _dropdownBtnRelease %W %x %y] bind $itk_component(list) \ [code $this _listShowing 1] bind $itk_component(list) \ [code $this _listShowing 0] # once in the listbox, we drop on the next release (unless in scrollbar) bind [_slbListbox] \ [code $this _ignoreNextBtnRelease false] bind $itk_component(arrowBtn) [code $this _drawArrow] bind $itk_component(arrowBtn) <3> [code $this _next] bind $itk_component(arrowBtn) [code $this _previous] bind $itk_component(arrowBtn) [code $this _next] bind $itk_component(arrowBtn) [code $this _previous] bind $itk_component(arrowBtn) [code $this _next] bind $itk_component(arrowBtn) [code $this _previous] bind $itk_component(arrowBtn) [code $this _toggleList] bind $itk_component(arrowBtn) [code $this _toggleList] bind $itk_component(arrowBtn) [code $this _toggleList] bind $itk_component(arrowBtn) [code $this _toggleList] bind $itk_component(entry) [code $this _resizeArrow] bind $itk_component(entry) [code $this _toggleList] bind $itk_component(entry) [code $this _toggleList] } # ------------------------------------------------------ # PROTECTED METHOD: _simpleBindings # # Bindings used only by the simple type Comboboxes. # # ------------------------------------------------------ body iwidgets::Combobox::_simpleBindings {} { bind [_slbListbox] [code $this _selectCmd] bind [_slbListbox] [code $this _selectCmd] bind [_slbListbox] [code $this _selectCmd] bind $itk_component(entry) "" bind $itk_component(entry) "" bind $itk_component(entry) "" bind $itk_component(entry) "" } # ------------------------------------------------------ # 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] } # ... emacs mode recognition ... # Local Variables: # mode: tcl # End: