source: trunk/gui/scripts/dropdownlist.tcl @ 2652

Last change on this file since 2652 was 1929, checked in by gah, 14 years ago
File size: 10.7 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: dropdownlist - drop-down list of items
3#
4#  This is a drop-down listbox, which might be used in conjunction
5#  with a combobox.
6#
7# ======================================================================
8#  AUTHOR:  Michael McLennan, Purdue University
9#  Copyright (c) 2004-2005  Purdue Research Foundation
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
16option add *Dropdownlist.textBackground white widgetDefault
17option add *Dropdownlist.outline black widgetDefault
18option add *Dropdownlist.borderwidth 1 widgetDefault
19option add *Dropdownlist.relief flat widgetDefault
20option add *Dropdownlist.font \
21    -*-helvetica-medium-r-normal-*-12-* widgetDefault
22
23itcl::class Rappture::Dropdownlist {
24    inherit Rappture::Dropdown
25
26    constructor {args} { # defined below }
27
28    public method insert {pos args}
29    public method delete {first {last ""}}
30    public method index {args}
31    public method get {args}
32    public method size {}
33    public method select {option args}
34    public method current {{what -value}}
35
36    private variable _values ""  ;# values for all elems in list
37    private variable _labels ""  ;# labels for each of the _values
38
39    protected method _adjust {{widget ""}}
40    protected method _react {}
41}
42
43itk::usual Dropdownlist {
44    keep -background -outline -cursor -font
45}
46
47# ----------------------------------------------------------------------
48# CONSTRUCTOR
49# ----------------------------------------------------------------------
50itcl::body Rappture::Dropdownlist::constructor {args} {
51    itk_component add scroller {
52        Rappture::Scroller $itk_interior.sc \
53            -xscrollmode off -yscrollmode auto
54    }
55    pack $itk_component(scroller) -expand yes -fill both
56
57    itk_component add list {
58        listbox $itk_component(scroller).list \
59            -selectmode single -exportselection no \
60            -highlightthickness 0
61    } {
62        usual
63        rename -background -textbackground textBackground Background
64        rename -foreground -textforeground textForeground Foreground
65        ignore -highlightthickness -highlightbackground -highlightcolor
66        keep -relief
67    }
68    $itk_component(scroller) contents $itk_component(list)
69
70    # add bindings so the listbox can react to selections
71    bind RapptureDropdownlist-$this <ButtonRelease-1> [itcl::code $this _react]
72    bind RapptureDropdownlist-$this <KeyPress-Return> [itcl::code $this _react]
73    bind RapptureDropdownlist-$this <KeyPress-space> [itcl::code $this _react]
74    bind RapptureDropdownlist-$this <KeyPress-Escape> [itcl::code $this unpost]
75
76    set btags [bindtags $itk_component(list)]
77    set i [lsearch $btags [winfo class $itk_component(list)]]
78    if {$i < 0} {
79        set i end
80    }
81    set btags [linsert $btags [expr {$i+1}] RapptureDropdownlist-$this]
82    bindtags $itk_component(list) $btags
83
84    eval itk_initialize $args
85}
86
87# ----------------------------------------------------------------------
88# USAGE: insert <pos> ?<value1> <label1> <value2> <label2> ...?
89#
90# Inserts one or more values into this drop-down list.  Each value
91# has a keyword (computer-friendly value) and a label (human-friendly
92# value).  The labels appear in the listbox.  If the label is "--",
93# then the value is used as the label.
94# ----------------------------------------------------------------------
95itcl::body Rappture::Dropdownlist::insert {pos args} {
96    if {"end" == $pos} {
97        set pos [llength $_values]
98    } elseif {![string is integer -strict $pos]} {
99        error "bad index \"$pos\": should be integer or \"end\""
100    }
101
102    if {[llength $args] == 1} {
103        set args [lindex $args 0]
104    }
105    if {[llength $args] % 2 != 0} {
106        error "wrong # args: should be \"insert pos ?value label ...?\""
107    }
108
109    foreach {val label} $args {
110        if {$label == "--"} {
111            set label $val
112        }
113        set _values [linsert $_values $pos $val]
114        set _labels [linsert $_labels $pos $label]
115        $itk_component(list) insert $pos $label
116        incr pos
117    }
118}
119
120# ----------------------------------------------------------------------
121# USAGE: delete <first> ?<last>?
122#
123# Deletes one or more values from this drop-down list.  The indices
124# <first> and <last> should be integers from 0 or the keyword "end".
125# All values in the specified range are deleted.
126# ----------------------------------------------------------------------
127itcl::body Rappture::Dropdownlist::delete {first {last ""}} {
128    if {$last == ""} {
129        set last $first
130    }
131    if {![regexp {^[0-9]+|end$} $first]} {
132        error "bad index \"$first\": should be integer or \"end\""
133    }
134    if {![regexp {^[0-9]+|end$} $last]} {
135        error "bad index \"$last\": should be integer or \"end\""
136    }
137
138    set _values [lreplace $_values $first $last]
139    set _labels [lreplace $_labels $first $last]
140    $itk_component(list) delete $first $last
141}
142
143# ----------------------------------------------------------------------
144# USAGE: index ?-value|-label? <value>
145#
146# Returns the integer index for the position of the specified <value>
147# in the list.  Returns -1 if the value is not recognized.
148# ----------------------------------------------------------------------
149itcl::body Rappture::Dropdownlist::index {args} {
150    set format -value
151    set first [lindex $args 0]
152    if {$first == "-value" || $first == "-label"} {
153        set format $first
154        set args [lrange $args 1 end]
155    } elseif {[llength $args] > 1} {
156        error "bad option \"$first\": should be -value or -label"
157    }
158    if {[llength $args] != 1} {
159        error "wrong # args: should be \"index ?-value? ?-label? string\""
160    }
161    set value [lindex $args 0]
162
163    switch -- $format {
164        -value { return [lsearch -exact $_values $value] }
165        -label { return [lsearch -exact $_labels $value] }
166    }
167    return -1
168}
169
170# ----------------------------------------------------------------------
171# USAGE: get ?-value|-label|-both? ?<index>?
172#
173# Queries one or more values from the drop-down list.  With no args,
174# it returns a list of all values and labels in the list.  If the
175# index is specified, then it returns the value or label (or both)
176# for the specified index.
177# ----------------------------------------------------------------------
178itcl::body Rappture::Dropdownlist::get {args} {
179    set format -both
180    set first [lindex $args 0]
181    if {[string index $first 0] == "-"} {
182        set choices {-value -label -both}
183        if {[lsearch $choices $first] < 0} {
184            error "bad option \"$first\": should be [join [lsort $choices] {, }]"
185        }
186        set format $first
187        set args [lrange $args 1 end]
188    }
189
190    # return the whole list or just a single value
191    if {[llength $args] > 1} {
192        error "wrong # args: should be \"get ?-value|-label|-both? ?index?\""
193    }
194    if {[llength $args] == 0} {
195        set vlist $_values
196        set llist $_labels
197        set op lappend
198    } else {
199        set i [lindex $args 0]
200        set vlist [list [lindex $_values $i]]
201        set llist [list [lindex $_labels $i]]
202        set op set
203    }
204
205    # scan through and build up the return list
206    set rlist ""
207    foreach v $vlist l $llist {
208        switch -- $format {
209            -value { $op rlist $v }
210            -label { $op rlist $l }
211            -both  { lappend rlist $v $l }
212        }
213    }
214    return $rlist
215}
216
217# ----------------------------------------------------------------------
218# USAGE: size
219#
220# Returns the number of choices in this list.
221# ----------------------------------------------------------------------
222itcl::body Rappture::Dropdownlist::size {} {
223    return [llength $_values]
224}
225
226# ----------------------------------------------------------------------
227# USAGE: select <option> ?<arg> ...?
228#
229# Used to manipulate the selection in the listbox.  All options and
230# args are passed along to the underlying listbox.
231# ----------------------------------------------------------------------
232itcl::body Rappture::Dropdownlist::select {option args} {
233    if {$option == "set"} {
234        $itk_component(list) activate [lindex $args 0]
235    }
236    eval $itk_component(list) selection $option $args
237}
238
239# ----------------------------------------------------------------------
240# USAGE: current ?-value|-label|-both?
241#
242# Clients use this to query the current selection from the listbox.
243# Returns the value, label, or both, according to the option.
244# ----------------------------------------------------------------------
245itcl::body Rappture::Dropdownlist::current {{what -value}} {
246    set i [$itk_component(list) curselection]
247    if {$i != ""} {
248        switch -- $what {
249            -value { return [lindex $_values $i] }
250            -label { return [lindex $_labels $i] }
251            -both  { return [list [lindex $_values $i] [lindex $_labels $i]] }
252            default {
253                error "bad option \"$what\": should be -value, -label, -both"
254            }
255        }
256    }
257    return ""
258}
259
260# ----------------------------------------------------------------------
261# USAGE: _adjust ?<widget>?
262#
263# This method is invoked each time the dropdown is posted to adjust
264# its size and contents.  If the <widget> is specified, it is the
265# controlling widget.
266# ----------------------------------------------------------------------
267itcl::body Rappture::Dropdownlist::_adjust {{widget ""}} {
268    chain $widget
269
270    set fnt [$itk_component(list) cget -font]
271    set maxw 0
272    foreach str $_labels {
273        set w [font measure $fnt $str]
274        if {$w > $maxw} { set maxw $w }
275    }
276    if {$widget != ""} {
277        if {$maxw < [winfo width $widget]} { set maxw [winfo width $widget] }
278    }
279    set avg [font measure $fnt "n"]
280    $itk_component(list) configure -width [expr {round($maxw/double($avg))+1}]
281
282    if {$widget != ""} {
283        set y [expr {[winfo rooty $widget]+[winfo height $widget]}]
284        set h [font metrics $fnt -linespace]
285        set lines [expr {double([winfo screenheight $widget]-$y)/$h}]
286        if {[llength $_labels] < $lines} {
287            $itk_component(list) configure -height [llength $_labels]
288        } else {
289            $itk_component(list) configure -height 10
290        }
291    }
292
293    focus $itk_component(list)
294}
295
296# ----------------------------------------------------------------------
297# USAGE: _react
298#
299# Invoked automatically when the user has selected something from
300# the listbox.  Unposts the drop-down and generates an event letting
301# the client know that the selection has changed.
302# ----------------------------------------------------------------------
303itcl::body Rappture::Dropdownlist::_react {} {
304    unpost
305    event generate $itk_component(hull) <<DropdownlistSelect>>
306}
Note: See TracBrowser for help on using the repository browser.