source: trunk/gui/scripts/combobox.tcl @ 6480

Last change on this file since 6480 was 6372, checked in by dkearney, 8 years ago

adding multichoice widget from the multichoice branch

File size: 12.8 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2# ----------------------------------------------------------------------
3#  COMPONENT: combobox - entry widget with a drop-down list of values
4#
5#  This widget is a typical combobox, an entry widget with a drop-down
6#  list of values.  If the -editable option is turned off, then the
7#  value can be set only from the drop-down list.  Otherwise, the
8#  drop-down is treated as a list of preset choices, but the user can
9#  type anything in the entry area.
10# ======================================================================
11#  AUTHOR:  Michael McLennan, Purdue University
12#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
13#
14#  See the file "license.terms" for information on usage and
15#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16# ======================================================================
17package require Itk
18package require BLT
19
20option add *Combobox.borderWidth 2 widgetDefault
21option add *Combobox.relief sunken widgetDefault
22option add *Combobox.width 10 widgetDefault
23option add *Combobox.editable yes widgetDefault
24option add *Combobox.textBackground white widgetDefault
25option add *Combobox.textForeground black widgetDefault
26option add *Combobox.disabledBackground white widgetDefault
27option add *Combobox.disabledForeground gray widgetDefault
28option add *Combobox.font -*-helvetica-medium-r-normal-*-12-* widgetDefault
29
30itcl::class Rappture::Combobox {
31    inherit itk::Widget
32
33    itk_option define -editable editable Editable ""
34    itk_option define -state state State "normal"
35    itk_option define -width width Width 0
36    itk_option define -disabledbackground disabledBackground DisabledBackground ""
37    itk_option define -disabledforeground disabledForeground DisabledForeground ""
38    itk_option define -interactcommand interactCommand InteractCommand ""
39
40    constructor {args} { # defined below }
41
42    public method value {args}
43    public method translate {value {defValue ""}}
44    public method label {value}
45    public method current {}
46    public method choices {option args}
47
48    protected method _entry {option}
49    protected method _dropdown {option}
50    protected method _fixState {}
51
52    blt::bitmap define ComboboxArrow {
53        #define arrow_width 8
54        #define arrow_height 4
55        static unsigned char arrow_bits[] = {
56           0xfe, 0x7c, 0x38, 0x10};
57    }
58    private variable _value2label
59    private variable _label2value
60}
61
62itk::usual Combobox {
63    keep -cursor -font
64    keep -foreground -background
65    keep -textforeground -textbackground
66    keep -selectbackground -selectforeground -selectborderwidth
67}
68
69# ----------------------------------------------------------------------
70# CONSTRUCTOR
71# ----------------------------------------------------------------------
72itcl::body Rappture::Combobox::constructor {args} {
73    itk_option add hull.borderwidth hull.relief
74
75    itk_component add button {
76        button $itk_interior.btn -bitmap ComboboxArrow -padx 0 \
77            -borderwidth 1 -relief raised -highlightthickness 0
78    } {
79        usual
80        ignore -highlightthickness -highlightbackground -highlightcolor
81        ignore -borderwidth -relief
82    }
83    pack $itk_component(button) -side right -fill y
84
85    itk_component add entry {
86        entry $itk_interior.entry -borderwidth 0 -relief flat
87    } {
88        usual
89        keep -width
90        rename -highlightbackground -textbackground textBackground Background
91        rename -background -textbackground textBackground Background
92        rename -foreground -textforeground textForeground Foreground
93        rename -disabledbackground -textbackground textBackground Background
94        rename -disabledforeground -textforeground textForeground Foreground
95        ignore -borderwidth -relief
96    }
97    pack $itk_component(entry) -side left -expand yes -fill both
98
99    bind $itk_component(entry) <KeyPress-Return> \
100        [itcl::code $this _entry apply]
101    bind $itk_component(entry) <ButtonPress> \
102        [itcl::code $this _entry click]
103
104    itk_component add ddlist {
105        Rappture::Dropdownlist $itk_component(button).ddlist \
106            -postcommand [itcl::code $this _dropdown post] \
107            -unpostcommand [itcl::code $this _dropdown unpost] \
108    }
109
110    bind $itk_component(ddlist) <<DropdownlistSelect>> \
111        [itcl::code $this _dropdown select]
112
113    $itk_component(button) configure -command \
114        [list $itk_component(ddlist) post $itk_component(hull) left]
115
116    eval itk_initialize $args
117}
118
119# ----------------------------------------------------------------------
120# USAGE: value ?<newval>?
121#
122# Clients use this to query/set the value for this widget.  With
123# no args, it returns the current value for the widget.  If the
124# <newval> is specified, it sets the value of the widget and
125# sends a <<Value>> event.
126# ----------------------------------------------------------------------
127itcl::body Rappture::Combobox::value {args} {
128    if {[llength $args] == 1} {
129        set newval [lindex $args 0]
130
131        $itk_component(entry) configure -state normal
132        $itk_component(entry) delete 0 end
133        $itk_component(entry) insert 0 $newval
134        if {!$itk_option(-editable)} {
135            $itk_component(entry) configure -state disabled
136        }
137
138        after 10 [list catch [list event generate $itk_component(hull) <<Value>>]]
139    } elseif {[llength $args] != 0} {
140        error "wrong # args: should be \"value ?newval?\""
141    }
142    return [$itk_component(entry) get]
143}
144
145# ----------------------------------------------------------------------
146# USAGE: translate <value>
147#
148# Clients use this to translate a value from the entry part of the
149# combobox to one of the underlying values in the combobox.  If the
150# <value> string matches one of the labels for the choices, this
151# method returns the corresponding value.  Otherwise, it returns "".
152# ----------------------------------------------------------------------
153itcl::body Rappture::Combobox::translate {value {defValue ""}} {
154    foreach {val label} [choices get -both] {
155        if {$label eq $value} {
156            return $val
157        }
158    }
159    return $defValue
160}
161
162# ----------------------------------------------------------------------
163# USAGE: label <value>
164#
165# Clients use this to translate a value to a label.
166# ----------------------------------------------------------------------
167itcl::body Rappture::Combobox::label { myValue } {
168    foreach {val label} [choices get -both] {
169        if {$myValue == $val} {
170            return $label
171        }
172    }
173    return ""
174}
175
176# ----------------------------------------------------------------------
177# USAGE: current
178#
179# Clients use this to retrieve a label from the entry widget and return
180# its value, if one exists. If no value exists for the label, then the
181# label is returned.
182# ----------------------------------------------------------------------
183itcl::body Rappture::Combobox::current {} {
184    set raw [$itk_component(entry) get]
185    set value [translate $raw "badValue"]
186    if { $value ne "badValue" } {
187        return $value
188    }
189    return $raw
190}
191
192# ----------------------------------------------------------------------
193# USAGE: choices insert <pos> ?<value1> <label1> ...?
194# USAGE: choices delete <first> ?<last>?
195# USAGE: choices get ?-value|-label|-both? ?<index>?
196# USAGE: choices index <value>
197#
198# Clients use this to manipulate the list of choices in the drop-down
199# list.  Each choice is represented by a (computer-friendly) value
200# and its corresponding (human-friendly) label.  The "get" option
201# returns information about options on the list, including the value,
202# the label, or both.
203# ----------------------------------------------------------------------
204itcl::body Rappture::Combobox::choices {option args} {
205    eval $itk_component(ddlist) $option $args
206}
207
208# ----------------------------------------------------------------------
209# USAGE: _entry apply
210# USAGE: _entry click
211#
212# Used internally to handle the dropdown list for this combobox.  The
213# post/unpost options are invoked when the list is posted or unposted
214# to manage the relief of the controlling button.  The select option
215# is invoked whenever there is a selection from the list, to assign
216# the value back to the gauge.
217# ----------------------------------------------------------------------
218itcl::body Rappture::Combobox::_entry {option} {
219    switch -- $option {
220        apply {
221            if {$itk_option(-editable) && $itk_option(-state) == "normal"} {
222                event generate $itk_component(hull) <<Value>>
223                if {[string length $itk_option(-interactcommand)] > 0} {
224                    uplevel #0 $itk_option(-interactcommand)
225                }
226            }
227        }
228        click {
229            if {!$itk_option(-editable) && $itk_option(-state) == "normal"} {
230                $itk_component(button) configure -relief sunken
231                update idletasks; after 100
232                $itk_component(button) configure -relief raised
233
234                $itk_component(ddlist) post $itk_component(hull) left
235            }
236        }
237        default {
238            error "bad option \"$option\": should be apply, click"
239        }
240    }
241}
242
243# ----------------------------------------------------------------------
244# USAGE: _dropdown post
245# USAGE: _dropdown unpost
246# USAGE: _dropdown select
247#
248# Used internally to handle the dropdown list for this combobox.  The
249# post/unpost options are invoked when the list is posted or unposted
250# to manage the relief of the controlling button.  The select option
251# is invoked whenever there is a selection from the list, to assign
252# the value back to the gauge.
253# ----------------------------------------------------------------------
254itcl::body Rappture::Combobox::_dropdown {option} {
255    switch -- $option {
256        post {
257            set value [$itk_component(entry) get]
258            set i [$itk_component(ddlist) index -label $value]
259            if {$i >= 0} {
260                $itk_component(ddlist) select clear 0 end
261                $itk_component(ddlist) select set $i
262            }
263        }
264        unpost {
265            if {$itk_option(-editable)} {
266                focus $itk_component(entry)
267            }
268        }
269        select {
270            set newval [$itk_component(ddlist) current -label]
271            set val [$itk_component(entry) get]
272            if {$newval ne "" && $newval ne $val} {
273                value $newval
274                if {[string length $itk_option(-interactcommand)] > 0} {
275                    uplevel #0 $itk_option(-interactcommand)
276                }
277            }
278        }
279        default {
280            error "bad option \"$option\": should be post, unpost, select"
281        }
282    }
283}
284
285# ----------------------------------------------------------------------
286# USAGE: _fixState
287#
288# Used internally to fix the widget state when the -editable/-state
289# options change.
290# ----------------------------------------------------------------------
291itcl::body Rappture::Combobox::_fixState {} {
292    if {$itk_option(-state) == "normal"} {
293        $itk_component(button) configure -state normal
294        $itk_component(entry) configure \
295            -background $itk_option(-textbackground) \
296            -foreground $itk_option(-textforeground) \
297            -disabledbackground $itk_option(-textbackground) \
298            -disabledforeground $itk_option(-textforeground)
299    } else {
300        $itk_component(button) configure -state disabled
301        $itk_component(entry) configure \
302            -background $itk_option(-disabledbackground) \
303            -foreground $itk_option(-disabledforeground) \
304            -disabledbackground $itk_option(-disabledbackground) \
305            -disabledforeground $itk_option(-disabledforeground)
306    }
307
308    if {$itk_option(-editable)} {
309        if {$itk_option(-state) == "normal"} {
310            $itk_component(entry) configure -state normal
311        } else {
312            $itk_component(entry) configure -state disabled
313        }
314    } else {
315        $itk_component(entry) configure -state disabled
316    }
317
318    if {!$itk_option(-editable) || $itk_option(-state) != "normal"} {
319        # can't keep focus here -- move it along to the next widget
320        if {[focus] == $itk_component(entry)} {
321            focus [tk_focusNext [focus]]
322        }
323    }
324}
325
326# ----------------------------------------------------------------------
327# CONFIGURATION OPTION: -editable
328# ----------------------------------------------------------------------
329itcl::configbody Rappture::Combobox::editable {
330    if {![string is boolean -strict $itk_option(-editable)]} {
331        error "bad value \"$itk_option(-editable)\": should be boolean"
332    }
333    _fixState
334}
335
336# ----------------------------------------------------------------------
337# CONFIGURATION OPTION: -state
338# ----------------------------------------------------------------------
339itcl::configbody Rappture::Combobox::state {
340    set valid {normal disabled}
341    if {[lsearch -exact $valid $itk_option(-state)] < 0} {
342        error "bad value \"$itk_option(-state)\": should be [join $valid {, }]"
343    }
344    _fixState
345}
Note: See TracBrowser for help on using the repository browser.