source: branches/r9/gui/scripts/combobox.tcl @ 5106

Last change on this file since 5106 was 3330, checked in by gah, 11 years ago

merge (by hand) with Rappture1.2 branch

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