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

Last change on this file since 3508 was 3330, checked in by gah, 12 years ago

merge (by hand) with Rappture1.2 branch

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