OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [itcl/] [iwidgets3.0.0/] [generic/] [combobox.itk] - Rev 1765

Compare with Previous | Blame | View Log

# 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
}

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.