1#
2#    This software is Copyright by the Board of Trustees of Michigan
3#    State University (c) Copyright 2005.
4#
5#    You may use this software under the terms of the GNU public license
6#    (GPL) ir the Tcl BSD derived license  The terms of these licenses
7#     are described at:
8#
9#     GPL:  http://www.gnu.org/licenses/gpl.txt
10#     Tcl:  http://www.tcl.tk/softare/tcltk/license.html
11#     Start with the second paragraph under the Tcl/Tk License terms
12#     as ownership is solely by Board of Trustees at Michigan State University.
13#
14#     Author:
15#            Ron Fox
16#            NSCL
17#            Michigan State University
18#            East Lansing, MI 48824-1321
19#
20
21
22#  Provide a megawidget that is a matrix of radio buttons
23#  and a variable that is tracked.  The idea is that this
24#  can be used to control a device that has an enumerable
25#  set of values.
26#
27# OPTIONS:
28#   -orient  Determines the order in which the radio buttons are
29#            laid out:
30#            vertical - buttons run from top to bottom then left to right.
31#            horizontal - buttons run from left to right top to bottom.
32#   -columns Number of columns.
33#   -rows    Number of rows.
34#   -values  Contains a list of values.  Each element of the list is either
35#            a single element, which represents the value of the button or
36#            is a pair of values that represent a name/value pair for the button.
37#            If -values is provided, only one of -rows/-columns can be provided.
38#            If -values is not provided, both -rows and -columns must be provided
39#            and the label name/value pairs are 1,2,3,4,5...
40#   -variable Variable to track in the widget.
41#   -command  Script to run when a radio button is clicked.
42#
43# METHODS:
44#    get   - Gets the current button value.
45#    set   - Sets the current button value (-command is invoked if defined).
46# NOTES:
47#   1. See the constraints on the options described above.
48#   2. If, on entry, the variable (either global or fully namespace qualified
49#      is set and matches a radio button value, that radio button is initially
50#      lit.
51#   3. The geometric properties of the widget can only be established at
52#      construction time, and are therefore static.
53
54package provide radioMatrix 1.0
55package require Tk
56package require snit
57package require bindDown
58
59namespace eval controlwidget {
60    namespace export radioMatrix
61}
62
63snit::widget  ::controlwidget::radioMatrix {
64
65    delegate option -variable to label as -textvariable
66    delegate option * to hull
67
68
69    option -orient   horizontal
70    option -rows     {1}
71    option -columns  {}
72    option -values   [list]
73    option -command  [list]
74
75
76    variable radioVariable;             # for the radio button.
77
78    # Construct the widget.
79
80    constructor args {
81
82        # The buttons go in a frame just to make it easy to lay them out.:
83
84        set bf [frame $win.buttons]
85        install label using label $win.label
86
87        # Process the configuration.
88
89        $self configurelist $args
90
91
92        # Ensure that the option constraints are met.
93
94        $self errorIfConstraintsNotMet
95
96        # If the values have not been provided, then use the rows/columns
97        # to simluate them.
98
99        if {$options(-values) eq ""}  {
100            set totalValues [expr $options(-columns) * $options(-rows)]
101            for {set i 0} {$i < $totalValues} {incr i} {
102                lappend options(-values) $i
103            }
104        }
105
106        # Top level layout decision based on orientation.
107
108        if {$options(-orient) eq "horizontal"} {
109            $self arrangeHorizontally
110        } elseif {$options(-orient) eq "vertical"} {
111            $self arrangeVertically
112        } else {
113            error "Invalid -orient value: $options(-orient)"
114        }
115
116        grid $bf
117        grid $win.label
118
119        # If the label has a text variable evaluate it to see
120        # if we can do a set with it:
121
122        set labelvar [$win.label cget -textvariable]
123        if {$labelvar ne ""} {
124            $self Set [set ::$labelvar]
125        }
126        bindDown $win $win
127    }
128
129    # Public methods:
130
131    method get {} {
132        return $radioVariable
133    }
134    method set value {
135
136        set radioVariable $value
137
138    }
139
140
141    # Private methods and procs.
142
143    # Ensure the constraints on the options are met.
144
145    method errorIfConstraintsNotMet {} {
146        if {$options(-values) eq "" &&
147            ($options(-rows) eq "" || $options(-columns) eq "")} {
148            error "If -values is not supplied, but -rows and -coumns must be."
149        }
150        if {($options(-rows) ne "" && $options(-columns) ne "") &&
151            $options(-values) ne ""} {
152            error "If both -rows and -coumns were supplied, -values cannot be"
153        }
154    }
155
156
157    # Process radio button change.
158    #
159    method onChange {} {
160        set script $options(-command)
161        if {$script ne ""} {
162            eval $script
163        }
164    }
165    # Manage horizontal layout
166
167    method arrangeHorizontally {} {
168        #
169        # Either both rows and columns are defined, or
170        # one is defined and the other must be computed from the
171        # length of the values list (which by god was defined).
172        # If both are defined, values was computed from them.
173
174        set rows $options(-rows)
175        set cols $options(-columns)
176
177        # Only really need # of cols.
178
179        set len  [llength $options(-values)]
180        if {$cols eq ""} {
181            set cols [expr ($len + $rows  - 1)/$rows]
182        }
183        set index  0
184        set rowNum 0
185
186        while {$index < $len} {
187            for {set i 0} {$i < $cols} {incr i} {
188                if {$index >= $len} {
189                    break
190                }
191                set item [lindex $options(-values) $index]
192
193                if {[llength $item] > 1} {
194                    set label [lindex $item 0]
195                    set value [lindex $item 1]
196                } else {
197                    set value [lindex $item 0]
198                    set label $value
199                }
200                radiobutton $win.buttons.cb$index \
201                    -command [mymethod onChange]  \
202                    -variable ${selfns}::radioVariable  \
203                    -value $value -text $label
204                grid $win.buttons.cb$index -row $rowNum -column $i
205                incr index
206            }
207            incr rowNum
208        }
209
210    }
211
212
213    # manage vertical layout
214
215    method arrangeVertically {} {
216        #
217        # See arrangeHorizontally for the overall picture, just swap cols
218        # and rows.
219
220        set rows $options(-rows)
221        set cols $options(-columns)
222
223        set len [llength $options(-values)]
224        if {$rows eq ""} {
225            set rows [expr ($len + $cols -1)/$cols]
226        }
227        set index  0
228        set colNum 0
229        while {$index < $len} {
230            for {set i 0} {$i < $rows} {incr i} {
231                if {$index >= $len} {
232                    break
233                }
234                set item [lindex $options(-values) $index]
235                if {[llength $item] > 1} {
236                    set label [lindex $item 0]
237                    set value [lindex $item 1]
238                } else {
239                    set value [lindex $item 0]
240                    set label $value
241                }
242
243                radiobutton $win.buttons.cb$index \
244                    -command [mymethod onChange]  \
245                    -variable ${selfns}::radioVariable \
246                    -value $value -text $label
247                grid $win.buttons.cb$index -row $i -column $colNum
248                incr index
249            }
250            incr colNum
251        }
252    }
253}
254