source: trunk/gui/scripts/choiceentry.tcl @ 3582

Last change on this file since 3582 was 3513, checked in by gah, 12 years ago

Add string trim to select 'xml get' calls

File size: 11.2 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2# ----------------------------------------------------------------------
3#  COMPONENT: ChoiceEntry - widget for entering a choice of strings
4#
5#  This widget represents a <choice> entry on a control panel.
6#  It is used to choose one of several mutually-exclusive strings.
7# ======================================================================
8#  AUTHOR:  Michael McLennan, Purdue University
9#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
10#
11#  See the file "license.terms" for information on usage and
12#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13# ======================================================================
14package require Itk
15
16itcl::class Rappture::ChoiceEntry {
17    inherit itk::Widget
18
19    itk_option define -state state State "normal"
20
21    constructor {owner path args} { # defined below }
22    destructor { # defined below }
23
24    public method value {args}
25
26    public method label {}
27    public method tooltip {}
28
29    protected method _rebuild {}
30    protected method _newValue {}
31    protected method _tooltip {}
32    protected method _log {}
33
34    private variable _owner ""    ;# thing managing this control
35    private variable _path ""     ;# path in XML to this number
36    private variable _str2val     ;# maps option label => option value
37}
38
39itk::usual ChoiceEntry {
40    keep -cursor -font
41    keep -foreground -background
42    keep -textforeground -textbackground
43    keep -selectbackground -selectforeground -selectborderwidth
44}
45
46# ----------------------------------------------------------------------
47# CONSTRUCTOR
48# ----------------------------------------------------------------------
49itcl::body Rappture::ChoiceEntry::constructor {owner path args} {
50    if {[catch {$owner isa Rappture::ControlOwner} valid] != 0 || !$valid} {
51        error "bad object \"$owner\": should be Rappture::ControlOwner"
52    }
53    set _owner $owner
54    set _path $path
55
56    #
57    # Create the widget and configure it properly based on other
58    # hints in the XML.
59    #
60    itk_component add choice {
61        Rappture::Combobox $itk_interior.choice -editable no \
62            -interactcommand [itcl::code $this _log]
63    }
64    pack $itk_component(choice) -expand yes -fill both
65    bind $itk_component(choice) <<Value>> [itcl::code $this _newValue]
66
67    eval itk_initialize $args
68
69    _rebuild
70}
71
72# ----------------------------------------------------------------------
73# DESTRUCTOR
74# ----------------------------------------------------------------------
75itcl::body Rappture::ChoiceEntry::destructor {} {
76    $_owner notify remove $this
77}
78
79# ----------------------------------------------------------------------
80# USAGE: value ?-check? ?<newval>?
81#
82# Clients use this to query/set the value for this widget.  With
83# no args, it returns the current value for the widget.  If the
84# <newval> is specified, it sets the value of the widget and
85# sends a <<Value>> event.  If the -check flag is included, the
86# new value is not actually applied, but just checked for correctness.
87# ----------------------------------------------------------------------
88itcl::body Rappture::ChoiceEntry::value {args} {
89    set onlycheck 0
90    set i [lsearch -exact $args -check]
91    if {$i >= 0} {
92        set onlycheck 1
93        set args [lreplace $args $i $i]
94    }
95
96    if {[llength $args] == 1} {
97        if {$onlycheck} {
98            # someday we may add validation...
99            return
100        }
101        set newval [lindex $args 0]
102        if {[info exists _str2val($newval)]} {
103            # this is a label -- use it directly
104            $itk_component(choice) value $newval
105            set newval $_str2val($newval)  ;# report the actual value
106        } else {
107            # this is a value -- search for corresponding label
108            foreach str [array names _str2val] {
109                if {$_str2val($str) eq $newval} {
110                    $itk_component(choice) value $str
111                    break
112                }
113            }
114        }
115        return $newval
116
117    } elseif {[llength $args] != 0} {
118        error "wrong # args: should be \"value ?-check? ?newval?\""
119    }
120
121    #
122    # Query the value and return.
123    #
124    set str [$itk_component(choice) value]
125    if {[info exists _str2val($str)]} {
126        return $_str2val($str)
127    }
128    return $str
129}
130
131# ----------------------------------------------------------------------
132# USAGE: label
133#
134# Clients use this to query the label associated with this widget.
135# Reaches into the XML and pulls out the appropriate label string.
136# ----------------------------------------------------------------------
137itcl::body Rappture::ChoiceEntry::label {} {
138    set label [$_owner xml get $_path.about.label]
139    if {"" == $label} {
140        set label "Choice"
141    }
142    return $label
143}
144
145# ----------------------------------------------------------------------
146# USAGE: tooltip
147#
148# Clients use this to query the tooltip associated with this widget.
149# Reaches into the XML and pulls out the appropriate description
150# string.  Returns the string that should be used with the
151# Rappture::Tooltip facility.
152# ----------------------------------------------------------------------
153itcl::body Rappture::ChoiceEntry::tooltip {} {
154    # query tooltip on-demand based on current choice
155    return "@[itcl::code $this _tooltip]"
156}
157
158# ----------------------------------------------------------------------
159# USAGE: _rebuild
160#
161# Used internally to rebuild the contents of this choice widget
162# whenever something that it depends on changes.  Scans through the
163# information in the XML spec and builds a list of choices for the
164# widget.
165# ----------------------------------------------------------------------
166itcl::body Rappture::ChoiceEntry::_rebuild {} {
167    # get rid of any existing choices
168    $itk_component(choice) choices delete 0 end
169    catch {unset _str2val}
170
171    #
172    # Plug in the various options for the choice.
173    #
174    set max 10
175    foreach cname [$_owner xml children -type option $_path] {
176        set path [string trim [$_owner xml get $_path.$cname.path]]
177        if {"" != $path} {
178            # look for the input element controlling this path
179            set found 0
180            foreach cntl [Rappture::entities [$_owner xml object] "input"] {
181                set len [string length $cntl]
182                if {[string equal -length $len $cntl $path]} {
183                    set found 1
184                    break
185                }
186            }
187            if {$found} {
188                #
189                # Choice comes from a list of matching entities at
190                # a particular XML path.  Use the <label> as a template
191                # for each item on the path.
192                #
193                $_owner notify add $this $cntl [itcl::code $this _rebuild]
194
195                set label [$_owner xml get $_path.$cname.about.label]
196                if {"" == $label} {
197                    set label "%type #%n"
198                }
199
200                set ppath [Rappture::LibraryObj::path2list $path]
201                set leading [join [lrange $ppath 0 end-1] .]
202                set tail [lindex $ppath end]
203                set n 1
204                foreach ccname [$_owner xml children $leading] {
205                    if {[string match $tail $ccname]} {
206                        set subst(%n) $n
207                        set subst(%type) [$_owner xml element -as type $leading.$ccname]
208                        set subst(%id) [$_owner xml element -as id $leading.$ccname]
209                        foreach detail [$_owner xml children $leading.$ccname] {
210                            set subst(%$detail) [$_owner xml get $leading.$ccname.$detail]
211                        }
212                        set str [string map [array get subst] $label]
213                        $itk_component(choice) choices insert end \
214                            $leading.$ccname $str
215                        incr n
216                    }
217                }
218                $itk_component(choice) value ""
219            } else {
220                puts "can't find controlling entity for path \"$path\""
221            }
222        } else {
223            #
224            # Choice is an ordinary LABEL.
225            # Add the label as-is into the list of choices.
226            #
227            set val [string trim [$_owner xml get $_path.$cname.value]]
228            set str [$_owner xml get $_path.$cname.about.label]
229            if {"" == $val} {
230                set val $str
231            }
232            if {"" != $str} {
233                set _str2val($str) $val
234                $itk_component(choice) choices insert end $_path.$cname $str
235                set len [string length $str]
236                if {$len > $max} { set max $len }
237            }
238        }
239    }
240    $itk_component(choice) configure -width $max
241
242    #
243    # Assign the default value to this widget, if there is one.
244    #
245    set defval [string trim [$_owner xml get $_path.default]]
246    if {"" != $defval} {
247        if {[info exists _str2val($defval)]} {
248            $itk_component(choice) value $defval
249        } else {
250            foreach str [array names _str2val] {
251                if {$_str2val($str) == $defval} {
252                    $itk_component(choice) value $str
253                    break
254                }
255            }
256        }
257    }
258}
259
260# ----------------------------------------------------------------------
261# USAGE: _newValue
262#
263# Invoked automatically whenever the value in the choice changes.
264# Sends a <<Value>> event to notify clients of the change.
265# ----------------------------------------------------------------------
266itcl::body Rappture::ChoiceEntry::_newValue {} {
267    event generate $itk_component(hull) <<Value>>
268}
269
270# ----------------------------------------------------------------------
271# USAGE: _tooltip
272#
273# Returns the tooltip for this widget, given the current choice in
274# the selector.  This is normally called by the Rappture::Tooltip
275# facility whenever it is about to pop up a tooltip for this widget.
276# ----------------------------------------------------------------------
277itcl::body Rappture::ChoiceEntry::_tooltip {} {
278    set tip [$_owner xml get $_path.about.description]
279
280    # get the description for the current choice, if there is one
281    set str [$itk_component(choice) value]
282    set path [$itk_component(choice) translate $str]
283    set desc ""
284    if {$path != ""} {
285        set desc [$_owner xml get $path.about.description]
286    }
287
288    if {[string length $str] > 0 && [string length $desc] > 0} {
289        append tip "\n\n$str:\n$desc"
290    }
291    return $tip
292}
293
294# ----------------------------------------------------------------------
295# USAGE: _log
296#
297# Used internally to send info to the logging mechanism.  Calls the
298# Rappture::Logger mechanism to log the change to this input.
299# ----------------------------------------------------------------------
300itcl::body Rappture::ChoiceEntry::_log {} {
301    Rappture::Logger::log input $_path [$itk_component(choice) value]
302}
303
304# ----------------------------------------------------------------------
305# CONFIGURATION OPTION: -state
306# ----------------------------------------------------------------------
307itcl::configbody Rappture::ChoiceEntry::state {
308    set valid {normal disabled}
309    if {[lsearch -exact $valid $itk_option(-state)] < 0} {
310        error "bad value \"$itk_option(-state)\": should be [join $valid {, }]"
311    }
312    $itk_component(choice) configure -state $itk_option(-state)
313}
Note: See TracBrowser for help on using the repository browser.