| # 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 |
| # ---------------------------------------------------------------------- |
| # CURRENT MAINTAINER: Chad Smith EMAIL: csmith@adc.com, itclguy@yahoo.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 |
| # ------------------------------------------------------------------ |
| itcl::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 variable _grab ;# used to restore grabs |
| private variable _next_prevFLAG 0 ;# Used in _lookup to fix SF Bug 501300 |
| 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 |
| # ------------------------------------------------------------------ |
| itcl::body iwidgets::Combobox::constructor {args} { |
| set _listShowing($this) 0 |
| set _grab(window) "" |
| set _grab(status) "" |
| |
| # 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 |
| # ------------------------------------------------------------------ |
| itcl::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. |
| # -------------------------------------------------------------------- |
| itcl::configbody iwidgets::Combobox::arrowrelief {} |
| |
| # -------------------------------------------------------------------- |
| # OPTION: -completion |
| # |
| # Relief style used on the arrow button. |
| # -------------------------------------------------------------------- |
| itcl::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. |
| # -------------------------------------------------------------------- |
| itcl::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. |
| # -------------------------------------------------------------------- |
| itcl::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 |
| # -------------------------------------------------------------------- |
| itcl::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.) |
| # -------------------------------------------------------------------- |
| itcl::configbody iwidgets::Combobox::listheight {} |
| |
| # -------------------------------------------------------------------- |
| # OPTION: -margin |
| # |
| # Spacer between the entry field and arrow button of dropdown style |
| # Comboboxes. |
| # -------------------------------------------------------------------- |
| itcl::configbody iwidgets::Combobox::margin { |
| grid columnconfigure $itk_interior 0 -minsize $itk_option(-margin) |
| } |
| |
| # -------------------------------------------------------------------- |
| # OPTION: -popupcursor |
| # |
| # Set the cursor for the popup list. |
| # -------------------------------------------------------------------- |
| itcl::configbody iwidgets::Combobox::popupcursor {} |
| |
| # -------------------------------------------------------------------- |
| # OPTION: -selectioncommand |
| # |
| # Defines the proc to be called when an item is selected in the list. |
| # -------------------------------------------------------------------- |
| itcl::configbody iwidgets::Combobox::selectioncommand {} |
| |
| # -------------------------------------------------------------------- |
| # OPTION: -state |
| # |
| # overall state of megawidget |
| # -------------------------------------------------------------------- |
| itcl::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 |
| } |
| } |
| } |
| readonly { |
| $itk_component(entry) configure -state readonly |
| } |
| 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. |
| # -------------------------------------------------------------------- |
| itcl::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). |
| # |
| # ------------------------------------------------------ |
| itcl::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. |
| # |
| # ------------------------------------------------------ |
| itcl::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. |
| # |
| # ------------------------------------------------------ |
| itcl::body iwidgets::Combobox::delete {component first {last {}}} { |
| switch -- $component { |
| entry { |
| if {$last == {}} { |
| set last [expr {$first + 1}] |
| } |
| 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 . |
| # |
| # ------------------------------------------------------ |
| itcl::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. |
| # |
| # ------------------------------------------------------ |
| itcl::body iwidgets::Combobox::getcurselection {} { |
| return [$itk_component(list) getcurselection] |
| } |
| |
| # ------------------------------------------------------------------ |
| # PUBLIC METHOD: invoke |
| # |
| # Pops up or down a dropdown combobox. |
| # |
| # ------------------------------------------------------------------ |
| itcl::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. |
| # |
| # ------------------------------------------------------------ |
| itcl::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 |
| [itcl::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. |
| # |
| # ------------------------------------------------------ |
| itcl::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. |
| # ------------------------------------------------------------------ |
| itcl::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. |
| # ------------------------------------------------------------------ |
| itcl::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. |
| # ------------------------------------------------------------------ |
| itcl::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? |
| # |
| # ------------------------------------------------------ |
| itcl::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. |
| # ------------------------------------------------------------------ |
| itcl::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. |
| # ------------------------------------------------------------------ |
| itcl::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. |
| # |
| # ------------------------------------------------------ |
| itcl::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. |
| # |
| # ------------------------------------------------------ |
| itcl::body iwidgets::Combobox::_createComponents {} { |
| if {$itk_option(-dropdown)} { |
| # --- build a dropdown combobox --- |
| |
| # make the arrow childsite be on the right hand side |
| |
| #------------------------------------------------------------- |
| # BUG FIX: csmith (Chad Smith: csmith@adc.com), 3/4/99 |
| #------------------------------------------------------------- |
| # The following commented line of code overwrites the -command |
| # option when passed into the constructor. The order of calls |
| # in the constructor is: |
| # 1) eval itk_initalize $args (initializes -command) |
| # 2) _doLayout |
| # 3) _createComponents (overwrites -command) |
| # The solution is to only set the -command option if it hasn't |
| # already been set. The following 4 lines of code do this. |
| #------------------------------------------------------------- |
| # ** configure -childsitepos e -command [code $this _addToList] |
| #------------------------------------------------------------- |
| configure -childsitepos e |
| if ![llength [cget -command]] { |
| configure -command [itcl::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 [itcl::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". |
| # |
| # ------------------------------------------------------ |
| itcl::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". |
| # |
| # ------------------------------------------------------ |
| itcl::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. |
| # |
| # ------------------------------------------------------ |
| itcl::body iwidgets::Combobox::_doLayout {{when later}} { |
| _createComponents |
| _packComponents $when |
| } |
| |
| |
| # ------------------------------------------------------ |
| # PROTECTED METHOD: _drawArrow |
| # |
| # Draw the arrow button. Determines packing according to |
| # -labelpos. |
| # |
| # ------------------------------------------------------ |
| itcl::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. |
| # |
| # ------------------------------------------------------ |
| itcl::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 |
| |
| # execute user command |
| if {$itk_option(-selectioncommand) != ""} { |
| uplevel #0 $itk_option(-selectioncommand) |
| } |
| } |
| |
| # ------------------------------------------------------ |
| # PROTECTED METHOD: _ignoreNextBtnRelease ignore |
| # |
| # Set private variable _ignoreRelease. If this variable |
| # is true then the next button release will not remove |
| # a dropdown list. |
| # |
| # ------------------------------------------------------ |
| itcl::body iwidgets::Combobox::_ignoreNextBtnRelease {ignore} { |
| set _ignoreRelease $ignore |
| } |
| |
| # ------------------------------------------------------ |
| # PROTECTED METHOD: _next |
| # |
| # Select the next item in the list. |
| # |
| # ------------------------------------------------------ |
| itcl::body iwidgets::Combobox::_next {} { |
| |
| set _next_prevFLAG 1 |
| |
| if {[size] <= 1} { |
| return |
| } |
| set i [curselection] |
| if {($i == {}) || ($i == ([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. |
| # |
| # ------------------------------------------------------ |
| itcl::body iwidgets::Combobox::_packComponents {{when later}} { |
| if {$when == "later"} { |
| if {$_repacking == ""} { |
| set _repacking [after idle [itcl::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. |
| # |
| # ------------------------------------------------------ |
| itcl::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 {(($y+$h) > $sh) && ($y > ($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. |
| # |
| # ------------------------------------------------------ |
| itcl::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 |
| |
| # Added by csmith, 12/19/00. Thanks to Erik Leunissen for |
| # finding this problem. We need to restore any previous |
| # grabs after the dropdown listbox is withdrawn. To do this, |
| # save the currently grabbed window. It is then restored in |
| # the _unpostList method. |
| set _grab(window) [::grab current] |
| if {$_grab(window) != ""} { |
| set _grab(status) [::grab status $_grab(window)] |
| } |
| |
| # Now grab the dropdown listbox. |
| if {$itk_option(-grab) == "global"} { |
| ::grab -global $itk_component(popup) |
| } else { |
| ::grab $itk_component(popup) |
| } |
| raise $itk_component(popup) |
| focus $itk_component(popup) |
| _drawArrow |
| |
| # Added by csmith, 10/26/00. This binding keeps the listbox |
| # from staying mapped if the window in which the combobox |
| # is packed is iconified. |
| bind $itk_component(entry) <Unmap> [itcl::code $this _unpostList] |
| } |
| |
| # ------------------------------------------------------ |
| # PROTECTED METHOD: _previous |
| # |
| # Select the previous item in the list. Wraps at front |
| # and end of list. |
| # |
| # ------------------------------------------------------ |
| itcl::body iwidgets::Combobox::_previous {} { |
| |
| set _next_prevFLAG 1 |
| |
| 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. |
| # |
| # ------------------------------------------------------ |
| itcl::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. |
| # |
| # ------------------------------------------------------ |
| itcl::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 |
| } |
| } |
| } |
| |
| # ------------------------------------------------------ |
| # PROTECTED METHOD: _toggleList |
| # |
| # Post or unpost the dropdown listbox (toggle). |
| # |
| # ------------------------------------------------------ |
| itcl::body iwidgets::Combobox::_toggleList {} { |
| if {[winfo ismapped $itk_component(popup)] } { |
| _unpostList |
| } else { |
| _postList |
| } |
| } |
| |
| # ------------------------------------------------------ |
| # PROTECTED METHOD: _unpostList |
| # |
| # Unmap the listbox (pop it down). |
| # |
| # ------------------------------------------------------ |
| itcl::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) |
| |
| # Added by csmith, 12/19/00. Thanks to Erik Leunissen for finding |
| # this problem. We need to restore any previous grabs when the |
| # dropdown listbox is unmapped. |
| if {$_grab(window) != ""} { |
| if {$_grab(status) == "global"} { |
| ::grab -global $_grab(window) |
| } else { |
| ::grab $_grab(window) |
| } |
| set _grab(window) "" |
| set _grab(status) "" |
| } |
| |
| # Added by csmith, 10/26/00. This binding resets the binding |
| # created in _postList - see that method for further details. |
| bind $itk_component(entry) <Unmap> {} |
| |
| 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 |
| update |
| } |
| |
| # ------------------------------------------------------ |
| # PROTECTED METHOD: _commonBindings |
| # |
| # Bindings that are used by both simple and dropdown |
| # style Comboboxes. |
| # |
| # ------------------------------------------------------ |
| itcl::body iwidgets::Combobox::_commonBindings {} { |
| bind $itk_component(entry) <KeyPress-BackSpace> [itcl::code $this _bs] |
| bind $itk_component(entry) <KeyRelease> [itcl::code $this _lookup %K] |
| bind $itk_component(entry) <Down> [itcl::code $this _next] |
| bind $itk_component(entry) <Up> [itcl::code $this _previous] |
| bind $itk_component(entry) <Control-n> [itcl::code $this _next] |
| bind $itk_component(entry) <Control-p> [itcl::code $this _previous] |
| bind [_slbListbox] <Control-n> [itcl::code $this _next] |
| bind [_slbListbox] <Control-p> [itcl::code $this _previous] |
| } |
| |
| |
| # ------------------------------------------------------ |
| # PROTECTED METHOD: _dropdownBindings |
| # |
| # Bindings used only by the dropdown type Combobox. |
| # |
| # ------------------------------------------------------ |
| itcl::body iwidgets::Combobox::_dropdownBindings {} { |
| bind $itk_component(popup) <Escape> [itcl::code $this _unpostList] |
| bind $itk_component(popup) <space> \ |
| "[itcl::code $this _stateSelect]; [itcl::code $this _unpostList]" |
| bind $itk_component(popup) <Return> \ |
| "[itcl::code $this _stateSelect]; [itcl::code $this _unpostList]" |
| bind $itk_component(popup) <ButtonRelease-1> \ |
| [itcl::code $this _dropdownBtnRelease %W %x %y] |
| |
| bind $itk_component(list) <Map> \ |
| [itcl::code $this _listShowing 1] |
| bind $itk_component(list) <Unmap> \ |
| [itcl::code $this _listShowing 0] |
| |
| # once in the listbox, we drop on the next release (unless in scrollbar) |
| bind [_slbListbox] <Enter> \ |
| [itcl::code $this _ignoreNextBtnRelease false] |
| |
| bind $itk_component(arrowBtn) <3> [itcl::code $this _next] |
| bind $itk_component(arrowBtn) <Shift-3> [itcl::code $this _previous] |
| bind $itk_component(arrowBtn) <Down> [itcl::code $this _next] |
| bind $itk_component(arrowBtn) <Up> [itcl::code $this _previous] |
| bind $itk_component(arrowBtn) <Control-n> [itcl::code $this _next] |
| bind $itk_component(arrowBtn) <Control-p> [itcl::code $this _previous] |
| bind $itk_component(arrowBtn) <Shift-Down> [itcl::code $this _toggleList] |
| bind $itk_component(arrowBtn) <Shift-Up> [itcl::code $this _toggleList] |
| bind $itk_component(arrowBtn) <Return> [itcl::code $this _toggleList] |
| bind $itk_component(arrowBtn) <space> [itcl::code $this _toggleList] |
| |
| bind $itk_component(entry) <Configure> [itcl::code $this _resizeArrow] |
| bind $itk_component(entry) <Shift-Down> [itcl::code $this _toggleList] |
| bind $itk_component(entry) <Shift-Up> [itcl::code $this _toggleList] |
| } |
| |
| # ------------------------------------------------------ |
| # PROTECTED METHOD: _simpleBindings |
| # |
| # Bindings used only by the simple type Comboboxes. |
| # |
| # ------------------------------------------------------ |
| itcl::body iwidgets::Combobox::_simpleBindings {} { |
| bind [_slbListbox] <ButtonRelease-1> [itcl::code $this _stateSelect] |
| bind [_slbListbox] <space> [itcl::code $this _stateSelect] |
| bind [_slbListbox] <Return> [itcl::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. |
| # ------------------------------------------------------ |
| itcl::body iwidgets::Combobox::_listShowing {{val ""}} { |
| if {$val == ""} { |
| return $_listShowing($this) |
| } elseif {$val == "-wait"} { |
| while {!$_listShowing($this)} { |
| tkwait variable [itcl::scope _listShowing($this)] |
| } |
| return |
| } |
| set _listShowing($this) $val |
| } |
| |
| # ------------------------------------------------------ |
| # PRIVATE METHOD: _slbListbox |
| # |
| # Access the tk listbox window out of the scrolledlistbox. |
| # |
| # ------------------------------------------------------ |
| itcl::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. |
| # |
| # ------------------------------------------------------ |
| itcl::body iwidgets::Combobox::_stateSelect {} { |
| switch -- $itk_option(-state) { |
| normal { |
| [itcl::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. |
| # |
| # ------------------------------------------------------ |
| itcl::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. |
| # |
| # ------------------------------------------------------ |
| itcl::body iwidgets::Combobox::_lookup {key} { |
| |
| # |
| # Don't process auto-completion stuff if navigation key was released |
| # Fixes SF bug 501300 |
| # |
| if {$_next_prevFLAG} { |
| set _next_prevFLAG 0 |
| return |
| } |
| |
| # |
| # 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 |
| } |
| |
| # No need to do lookups for Shift keys or Arrows. The up/down |
| # arrow keys should walk up/down the listbox entries. |
| switch $key { |
| Shift_L - Shift_R - Up - Down - Left - Right { |
| set _inlookup 0 |
| return |
| } |
| default { } |
| } |
| |
| # Added by csmith 12/11/01 to resolve SF ticket #474817. It's an unusual |
| # circumstance, but we need to make sure the character passed into this |
| # method matches the last character in the entry's text string. It's |
| # possible to type fast enough that the _lookup method gets invoked |
| # *after* multiple characters have been typed and *before* the first |
| # character has been processed. For example, you can type "bl" very |
| # quickly, and by the time the interpreter processes "b", the "l" has |
| # already been placed in the entry field. This causes problems as noted |
| # in the SF ticket. |
| # |
| # Thus, if the character currently being processed does not match the |
| # last character in the entry field, reset the _inlookup flag and return. |
| # Also, note that we're only concerned with single characters here, not |
| # keys such as backspace, delete, etc. |
| if {$key != [string range $text end end] && [string match ? $key]} { |
| 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 |
| } |