source: trunk/gui/scripts/combochecks.tcl @ 6372

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

adding multichoice widget from the multichoice branch

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