source: trunk/gui/scripts/resultset.tcl @ 654

Last change on this file since 654 was 464, checked in by mmc, 18 years ago

Added popup options for the "download" button. Right now this works
only for <curve> objects. You can select between CSV and PDF output.
Will add other formats later.

Fixed a few "after cancel" errors that were happening when you switch
between inputs in the structure demo.

Fixed the colors and fonts for the new bug report window.

File size: 57.7 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: ResultSet - controls for a collection of related results
3#
4#  This widget stores a collection of results that all represent
5#  the same quantity, but for various ranges of input values.
6#  It also manages the controls to select and visualize the data.
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 *ResultSet.width 4i widgetDefault
17option add *ResultSet.height 4i widgetDefault
18option add *ResultSet.missingData skip widgetDefault
19option add *ResultSet.controlbarBackground gray widgetDefault
20option add *ResultSet.controlbarForeground white widgetDefault
21option add *ResultSet.activeControlBackground #ffffcc widgetDefault
22option add *ResultSet.activeControlForeground black widgetDefault
23option add *ResultSet.controlActiveForeground blue widgetDefault
24option add *ResultSet.toggleBackground gray widgetDefault
25option add *ResultSet.toggleForeground white widgetDefault
26option add *ResultSet.textFont \
27    -*-helvetica-medium-r-normal-*-*-120-* widgetDefault
28option add *ResultSet.boldFont \
29    -*-helvetica-bold-r-normal-*-*-120-* widgetDefault
30
31itcl::class Rappture::ResultSet {
32    inherit itk::Widget
33
34    itk_option define -activecontrolbackground activeControlBackground Background ""
35    itk_option define -activecontrolforeground activeControlForeground Foreground ""
36    itk_option define -controlactiveforeground controlActiveForeground Foreground ""
37    itk_option define -togglebackground toggleBackground Background ""
38    itk_option define -toggleforeground toggleForeground Foreground ""
39    itk_option define -textfont textFont Font ""
40    itk_option define -boldfont boldFont Font ""
41    itk_option define -foreground foreground Foreground ""
42    itk_option define -missingdata missingData MissingData ""
43    itk_option define -clearcommand clearCommand ClearCommand ""
44    itk_option define -settingscommand settingsCommand SettingsCommand ""
45    itk_option define -promptcommand promptCommand PromptCommand ""
46
47    constructor {args} { # defined below }
48    destructor { # defined below }
49
50    public method add {xmlobj}
51    public method clear {}
52    public method activate {column}
53    public method contains {xmlobj}
54    public method size {{what -results}}
55
56    protected method _doClear {}
57    protected method _doSettings {{cmd ""}}
58    protected method _doPrompt {state}
59    protected method _control {option args}
60    protected method _fixControls {args}
61    protected method _fixLayout {args}
62    protected method _fixSettings {args}
63    protected method _fixExplore {}
64    protected method _fixValue {column why}
65    protected method _drawValue {column widget wmax}
66    protected method _toggleAll {{column "current"}}
67    protected method _getTooltip {role column}
68    protected method _getParamDesc {{index "current"}}
69
70    private variable _dispatcher ""  ;# dispatchers for !events
71    private variable _results ""     ;# tuple of known results
72    private variable _recent ""      ;# most recent result in _results
73    private variable _active ""      ;# column with active control
74    private variable _plotall 0      ;# non-zero => plot all active results
75    private variable _layout         ;# info used in _fixLayout
76    private variable _counter 0      ;# counter for unique control names
77    private variable _settings 0     ;# non-zero => _fixSettings in progress
78    private variable _explore 0      ;# non-zero => explore all parameters
79
80    private common _cntlInfo         ;# maps column name => control info
81}
82                                                                               
83itk::usual ResultSet {
84    keep -background -foreground -cursor -font
85}
86
87# ----------------------------------------------------------------------
88# CONSTRUCTOR
89# ----------------------------------------------------------------------
90itcl::body Rappture::ResultSet::constructor {args} {
91    option add hull.width hull.height
92    pack propagate $itk_component(hull) no
93
94    # create a dispatcher for events
95    Rappture::dispatcher _dispatcher
96    $_dispatcher register !fixcntls
97    $_dispatcher dispatch $this !fixcntls \
98        [itcl::code $this _fixControls]
99    $_dispatcher register !layout
100    $_dispatcher dispatch $this !layout \
101        [itcl::code $this _fixLayout]
102    $_dispatcher register !settings
103    $_dispatcher dispatch $this !settings \
104        [itcl::code $this _fixSettings]
105
106    # initialize controls info
107    set _cntlInfo($this-all) ""
108
109    # initialize layout info
110    set _layout(mode) "usual"
111    set _layout(active) ""
112
113    # create a list of tuples for data
114    set _results [Rappture::Tuples ::#auto]
115    $_results column insert end -name xmlobj -label "top-level XML object"
116
117
118    itk_component add cntls {
119        frame $itk_interior.cntls
120    } {
121        usual
122        rename -background -controlbarbackground controlbarBackground Background
123        rename -highlightbackground -controlbarbackground controlbarBackground Background
124    }
125    pack $itk_component(cntls) -fill x -pady {0 2}
126
127    itk_component add clear {
128        button $itk_component(cntls).clear -text "Clear" -state disabled \
129            -padx 1 -pady 1 \
130            -relief flat -overrelief raised \
131            -command [itcl::code $this _doClear]
132    } {
133        usual
134        rename -background -controlbarbackground controlbarBackground Background
135        rename -foreground -controlbarforeground controlbarForeground Foreground
136        rename -highlightbackground -controlbarbackground controlbarBackground Background
137    }
138    pack $itk_component(clear) -side right -padx 2 -pady 1
139    Rappture::Tooltip::for $itk_component(clear) \
140        "Clears all results collected so far."
141
142    itk_component add status {
143        label $itk_component(cntls).status -anchor w \
144            -text "No results" -padx 0 -pady 0
145    } {
146        usual
147        rename -background -controlbarbackground controlbarBackground Background
148        rename -foreground -controlbarforeground controlbarForeground Foreground
149        rename -highlightbackground -controlbarbackground controlbarBackground Background
150    }
151    pack $itk_component(status) -side left -padx 2 -pady {2 0}
152
153    itk_component add parameters {
154        button $itk_component(cntls).params -text "Parameters..." \
155            -state disabled -padx 1 -pady 1 \
156            -relief flat -overrelief raised \
157            -command [list $itk_component(hull).popup activate $itk_component(cntls).params above]
158    } {
159        usual
160        rename -background -controlbarbackground controlbarBackground Background
161        rename -foreground -controlbarforeground controlbarForeground Foreground
162        rename -highlightbackground -controlbarbackground controlbarBackground Background
163    }
164    pack $itk_component(parameters) -side left -padx 8 -pady 1
165    Rappture::Tooltip::for $itk_component(parameters) \
166        "Click to access all parameters."
167
168    itk_component add dials {
169        frame $itk_interior.dials
170    }
171    pack $itk_component(dials) -expand yes -fill both
172    bind $itk_component(dials) <Configure> \
173        [list $_dispatcher event -after 10 !layout why resize]
174
175    # create the permanent controls in the "short list" area
176    set dials $itk_component(dials)
177    frame $dials.bg
178    Rappture::Radiodial $dials.dial -valuewidth 0
179    Rappture::Tooltip::for $dials.dial \
180        "@[itcl::code $this _getTooltip dial active]"
181
182    set fn [option get $itk_component(hull) textFont Font]
183    label $dials.all -text "All" -padx 8 \
184        -borderwidth 1 -relief raised -font $fn
185    Rappture::Tooltip::for $dials.all \
186        "@[itcl::code $this _getTooltip all active]"
187    bind $dials.all <ButtonRelease> [itcl::code $this _toggleAll]
188
189    frame $dials.labelmore
190    label $dials.labelmore.arrow -bitmap [Rappture::icon empty] -borderwidth 0
191    pack $dials.labelmore.arrow -side left -fill y
192    _control bind $dials.labelmore.arrow @more
193    label $dials.labelmore.name -text "more parameters..." -font $fn \
194        -borderwidth 0 -padx 0 -pady 1
195    pack $dials.labelmore.name -side left
196    label $dials.labelmore.value
197    pack $dials.labelmore.value -side left
198    _control bind $dials.labelmore.name @more
199    Rappture::Tooltip::for $dials.labelmore \
200        "@[itcl::code $this _getTooltip more more]"
201
202    # use this pop-up for access to all controls
203    Rappture::Balloon $itk_component(hull).popup \
204        -title "Change Parameters" -padx 0 -pady 0
205    set inner [$itk_component(hull).popup component inner]
206
207    frame $inner.cntls
208    pack $inner.cntls -side bottom -fill x
209    frame $inner.cntls.sep -height 2 -borderwidth 1 -relief sunken
210    pack $inner.cntls.sep -side top -fill x -padx 4 -pady 4
211    checkbutton $inner.cntls.explore -font $fn \
212        -text "Explore combinations with no results" \
213        -variable [itcl::scope _explore] \
214        -command [itcl::code $this _fixExplore]
215    pack $inner.cntls.explore -side top -anchor w
216    Rappture::Tooltip::for $inner.cntls.explore \
217        "When this option is turned on, you can set parameters to various combinations that have not yet been simulated.  The Simulate button will light up, and you can simulate these missing combinations.\n\nWhen turned off, controls will avoid missing combinations, and automatically snap to the closest available dataset."
218
219    itk_component add options {
220        Rappture::Scroller $inner.scrl -xscrollmode auto -yscrollmode auto
221    }
222    pack $itk_component(options) -expand yes -fill both
223
224    set popup [$itk_component(options) contents frame]
225    frame $popup.bg
226
227    eval itk_initialize $args
228}
229
230# ----------------------------------------------------------------------
231# DESTRUCTOR
232# ----------------------------------------------------------------------
233itcl::body Rappture::ResultSet::destructor {} {
234    itcl::delete object $_results
235}
236
237# ----------------------------------------------------------------------
238# USAGE: add <xmlobj>
239#
240# Adds a new result to this result set.  Scans through all existing
241# results to look for a difference compared to previous results.
242# Returns the index of this new result to the caller.  The various
243# data objects for this result set should be added to their result
244# viewers at the same index.
245# ----------------------------------------------------------------------
246itcl::body Rappture::ResultSet::add {xmlobj} {
247    # make sure we fix up controls at some point
248    $_dispatcher event -idle !fixcntls
249
250    #
251    # If this is the first result, then there are no diffs.
252    # Add it right in.
253    #
254    set xmlobj0 [$_results get -format xmlobj end]
255    if {"" == $xmlobj0} {
256        # first element -- always add
257        $_results insert end [list $xmlobj]
258        set _recent $xmlobj
259        $itk_component(status) configure -text "1 result"
260        $itk_component(clear) configure -state normal
261        if {[$_results size] >= 2} {
262            $itk_component(parameters) configure -state normal
263        } else {
264            $itk_component(parameters) configure -state disabled
265        }
266        return 0
267    }
268
269    #
270    # Compare this new object against the last XML object in the
271    # results set.  If it has a difference, make sure that there
272    # is a column to represent the quantity with the difference.
273    #
274    foreach {op vpath oldval newval} [$xmlobj0 diff $xmlobj] {
275        if {[$xmlobj get $vpath.about.diffs] == "ignore"} {
276            continue
277        }
278        if {$op == "+" || $op == "-"} {
279            # ignore differences where parameters come and go
280            # such differences make it hard to work controls
281            continue
282        }
283        if {[$_results column names $vpath] == ""} {
284            # no column for this quantity yet
285            $_results column insert end -name $vpath -default $oldval
286        }
287    }
288
289    # build a tuple for this new object
290    set cols ""
291    set tuple ""
292    foreach col [lrange [$_results column names] 1 end] {
293        lappend cols $col
294        set raw [lindex [Rappture::LibraryObj::value $xmlobj $col] 0]
295        lappend tuple $raw  ;# use the "raw" (user-readable) label
296    }
297
298    # find a matching tuple? then replace it -- only need one
299    if {[llength $cols] > 0} {
300        set ilist [$_results find -format $cols -- $tuple]
301    } else {
302        set ilist 0  ;# no diffs -- must match first entry
303    }
304
305    # add all remaining columns for this new entry
306    set tuple [linsert $tuple 0 $xmlobj]
307
308    if {[llength $ilist] > 0} {
309        if {[llength $ilist] > 1} {
310            error "why so many matching results?"
311        }
312
313        # overwrite the first matching entry
314        set index [lindex $ilist 0]
315        $_results put $index $tuple
316        set _recent $xmlobj
317    } else {
318        set index [$_results size]
319        $_results insert end $tuple
320        set _recent $xmlobj
321    }
322
323    if {[$_results size] == 1} {
324        $itk_component(status) configure -text "1 result"
325    } else {
326        $itk_component(status) configure -text "[$_results size] results"
327        $itk_component(parameters) configure -state normal
328    }
329    $itk_component(clear) configure -state normal
330
331    return $index
332}
333
334# ----------------------------------------------------------------------
335# USAGE: clear
336#
337# Clears all results in this result set.
338# ----------------------------------------------------------------------
339itcl::body Rappture::ResultSet::clear {} {
340    _doSettings
341
342    # delete all adjuster controls
343    set popup [$itk_component(options) contents frame]
344    set shortlist $itk_component(dials)
345    foreach col $_cntlInfo($this-all) {
346        set id $_cntlInfo($this-$col-id)
347        destroy $popup.label$id $popup.dial$id $popup.all$id
348        destroy $shortlist.label$id
349    }
350
351    # clean up control info
352    foreach key [array names _cntlInfo $this-*] {
353        catch {unset _cntlInfo($key)}
354    }
355    set _cntlInfo($this-all) ""
356    set _counter 0
357
358    # clear out all results
359    $_results delete 0 end
360    eval $_results column delete [lrange [$_results column names] 1 end]
361    set _recent ""
362    set _active ""
363
364    set _plotall 0
365    $itk_component(dials).all configure -relief raised \
366        -background $itk_option(-background) \
367        -foreground $itk_option(-foreground)
368
369    # update status and Clear button
370    $itk_component(status) configure -text "No results"
371    $itk_component(parameters) configure -state disabled
372    $itk_component(clear) configure -state disabled
373    $_dispatcher event -idle !fixcntls
374
375    # let clients know that the number of controls has changed
376    event generate $itk_component(hull) <<Control>>
377}
378
379# ----------------------------------------------------------------------
380# USAGE: activate <column>
381#
382# Clients use this to activate a particular column in the set of
383# controls.  When a column is active, its label is bold and its
384# value has a radiodial in the "short list" area.
385# ----------------------------------------------------------------------
386itcl::body Rappture::ResultSet::activate {column} {
387    if {$column == "@more"} {
388        $itk_component(hull).popup activate \
389            $itk_component(dials).labelmore.name above
390        return
391    }
392
393    set allowed [$_results column names]
394    if {[lsearch $allowed $column] < 0} {
395        error "bad value \"$column\": should be one of [join $allowed {, }]"
396    }
397
398    # column is now active
399    set _active $column
400
401    # keep track of usage, so we know which controls are popular
402    incr _cntlInfo($this-$column-usage)
403
404    # fix controls at next idle point
405    $_dispatcher event -idle !layout why data
406    $_dispatcher event -idle !settings column $_active
407}
408
409# ----------------------------------------------------------------------
410# USAGE: contains <xmlobj>
411#
412# Checks to see if the given <xmlobj> is already represented by
413# some result in this result set.  This comes in handy when checking
414# to see if an input case is already covered.
415#
416# Returns 1 if the result set already contains this result, and
417# 0 otherwise.
418# ----------------------------------------------------------------------
419itcl::body Rappture::ResultSet::contains {xmlobj} {
420    # no results? then this must be new
421    if {[$_results size] == 0} {
422        return 0
423    }
424
425    #
426    # Compare this new object against the last XML object in the
427    # results set.  If it has a difference, make sure that there
428    # is a column to represent the quantity with the difference.
429    #
430    set xmlobj0 [$_results get -format xmlobj end]
431    foreach {op vpath oldval newval} [$xmlobj0 diff $xmlobj] {
432        if {[$xmlobj get $vpath.about.diffs] == "ignore"} {
433            continue
434        }
435        if {$op == "+" || $op == "-"} {
436            # ignore differences where parameters come and go
437            # such differences make it hard to work controls
438            continue
439        }
440        if {[$_results column names $vpath] == ""} {
441            # no column for this quantity yet
442            return 0
443        }
444    }
445
446    #
447    # If we got this far, then look through existing results for
448    # matching tuples, then check each one for diffs.
449    #
450    set format ""
451    set tuple ""
452    foreach col [lrange [$_results column names] 1 end] {
453        lappend format $col
454        set raw [lindex [Rappture::LibraryObj::value $xmlobj $col] 0]
455        lappend tuple $raw  ;# use the "raw" (user-readable) label
456    }
457    if {[llength $format] > 0} {
458        set ilist [$_results find -format $format -- $tuple]
459    } else {
460        set ilist 0  ;# no diffs -- must match first entry
461    }
462
463    foreach i $ilist {
464        set xmlobj0 [$_results get -format xmlobj $i]
465        set diffs [$xmlobj0 diff $xmlobj]
466        if {[llength $diffs] == 0} {
467            # no diffs -- already contained here
468            return 1
469        }
470    }
471
472    # must be some differences
473    return 0
474}
475
476
477# ----------------------------------------------------------------------
478# USAGE: size ?-results|-controls|-controlarea?
479#
480# Returns various measures for the size of this area:
481#   -results ....... number of results loaded
482#   -controls ...... number of distinct control parameters
483#   -controlarea ... minimum size of usable control area, in pixels
484# ----------------------------------------------------------------------
485itcl::body Rappture::ResultSet::size {{what -results}} {
486    switch -- $what {
487        -results {
488            return [$_results size]
489        }
490        -controls {
491            return [llength $_cntlInfo($this-all)]
492        }
493        -controlarea {
494            set ht [winfo reqheight $itk_component(cntls)]
495            incr ht 2  ;# padding below controls
496
497            set normalLine [font metrics $itk_option(-textfont) -linespace]
498            incr normalLine 2  ;# padding
499            set boldLine [font metrics $itk_option(-boldfont) -linespace]
500            incr boldLine 2  ;# padding
501
502            set numcntls [llength $_cntlInfo($this-all)]
503            switch -- $numcntls {
504                0 - 1 {
505                    # 0 = no controls (no data at all)
506                    # 1 = run control, but only 1 run so far
507                    # add nothing
508                }
509                default {
510                    # non-active controls
511                    incr ht [expr {($numcntls-1)*$normalLine}]
512                    # active control
513                    incr ht $boldLine
514                    # dial for active control
515                    incr ht [winfo reqheight $itk_component(dials).dial]
516                    # padding around active control
517                    incr ht 4
518                }
519            }
520            return $ht
521        }
522        default {
523            error "bad option \"$what\": should be -results, -controls, or -controlarea"
524        }
525    }
526}
527
528# ----------------------------------------------------------------------
529# USAGE: _doClear
530#
531# Invoked automatically when the user presses the Clear button.
532# Invokes the -clearcommand to clear all data from this resultset
533# and all other resultsets in an Analyzer.
534# ----------------------------------------------------------------------
535itcl::body Rappture::ResultSet::_doClear {} {
536    if {[string length $itk_option(-clearcommand)] > 0} {
537        uplevel #0 $itk_option(-clearcommand)
538    }
539}
540
541# ----------------------------------------------------------------------
542# USAGE: _doSettings ?<command>?
543#
544# Used internally whenever the result selection changes to invoke
545# the -settingscommand.  This will notify some external widget, which
546# with perform the plotting action specified in the <command>.
547# ----------------------------------------------------------------------
548itcl::body Rappture::ResultSet::_doSettings {{cmd ""}} {
549    if {[string length $itk_option(-settingscommand)] > 0} {
550        uplevel #0 $itk_option(-settingscommand) $cmd
551    }
552}
553
554# ----------------------------------------------------------------------
555# USAGE: _doPrompt <state>
556#
557# Used internally whenever the current settings represent a point
558# with no data.  Invokes the -promptcommand with an explanation of
559# the missing data, prompting the user to simulate it.
560# ----------------------------------------------------------------------
561itcl::body Rappture::ResultSet::_doPrompt {state} {
562    if {[string length $itk_option(-promptcommand)] > 0} {
563        if {$state} {
564            set message "No data for these settings"
565            set settings ""
566            foreach col [lrange [$_results column names] 1 end] {
567                set val $_cntlInfo($this-$col-value)
568                lappend settings $col $val
569            }
570            uplevel #0 $itk_option(-promptcommand) [list on $message $settings]
571        } else {
572            uplevel #0 $itk_option(-promptcommand) off
573        }
574    }
575}
576
577# ----------------------------------------------------------------------
578# USAGE: _control bind <widget> <column>
579# USAGE: _control hilite <state> <column> <panel>
580# USAGE: _control load <widget> <column>
581#
582# Used internally to manage the interactivity of controls.  The "bind"
583# operation sets up bindings on the label/value for each control, so
584# you can mouse over and click on a control to activate it.  The
585# "hilite" operation controls highlighting of the control.  The "load"
586# operation loads data into the specified radiodial <widget>.
587# ----------------------------------------------------------------------
588itcl::body Rappture::ResultSet::_control {option args} {
589    switch -- $option {
590        bind {
591            if {[llength $args] != 2} {
592                error "wrong # args: should be _control bind widget column"
593            }
594            set widget [lindex $args 0]
595            set col [lindex $args 1]
596
597            set panel [winfo parent $widget]
598            if {[string match label* [winfo name $panel]]} {
599                set panel [winfo parent $panel]
600            }
601
602            bind $widget <Enter> \
603                [itcl::code $this _control hilite on $col $panel]
604            bind $widget <Leave> \
605                [itcl::code $this _control hilite off $col $panel]
606            bind $widget <ButtonRelease> [itcl::code $this activate $col]
607        }
608        hilite {
609            if {[llength $args] != 3} {
610                error "wrong # args: should be _control hilite state column panel"
611            }
612            if {$_layout(mode) != "usual"} {
613                # abbreviated controls? then skip highlighting
614                return
615            }
616            set state [lindex $args 0]
617            set col [lindex $args 1]
618            set panel [lindex $args 2]
619
620            if {[string index $col 0] == "@"} {
621                # handle artificial names like "@more"
622                set id [string range $col 1 end]
623            } else {
624                # get id for ordinary columns
625                set id $_cntlInfo($this-$col-id)
626            }
627
628            # highlight any non-active entries
629            if {$col != $_active} {
630                if {$state} {
631                    set fg $itk_option(-controlactiveforeground)
632                    $panel.label$id.name configure -fg $fg
633                    $panel.label$id.value configure -fg $fg
634                    $panel.label$id.arrow configure -fg $fg \
635                        -bitmap [Rappture::icon rarrow2]
636                } else {
637                    set fg $itk_option(-foreground)
638                    $panel.label$id.name configure -fg $fg
639                    $panel.label$id.value configure -fg $fg
640                    $panel.label$id.arrow configure -fg $fg \
641                        -bitmap [Rappture::icon empty]
642                }
643            }
644        }
645        load {
646            if {[llength $args] != 2} {
647                error "wrong # args: should be _control load widget column"
648            }
649            set dial [lindex $args 0]
650            set col [lindex $args 1]
651
652            if {$col == "xmlobj"} {
653                 # load the Simulation # control
654                 set nruns [$_results size]
655
656                 # load the results into the control
657                 $dial clear
658                 for {set n 0} {$n < $nruns} {incr n} {
659                     $dial add "#[expr {$n+1}]" $n
660                 }
661                 return
662            }
663
664            set havenums 1
665            set vlist ""
666            foreach rec [$_results get -format [list xmlobj $col]] {
667                set xo [lindex $rec 0]
668                set v [lindex $rec 1]
669
670                if {![info exists values($v)]} {
671                    lappend vlist $v
672                    foreach {raw norm} [Rappture::LibraryObj::value $xo $col] break
673                    set values($v) $norm
674
675                    if {$havenums && ![string is double $norm]} {
676                        set havenums 0
677                    }
678                }
679            }
680
681            if {!$havenums} {
682                # don't have normalized nums? then sort and create nums
683                catch {unset values}
684
685                set n 0
686                foreach v [lsort $vlist] {
687                    set values($v) [incr n]
688                }
689            }
690
691            # load the results into the control
692            $dial clear
693            foreach v [array names values] {
694                $dial add $v $values($v)
695            }
696        }
697        default {
698            error "bad option \"$option\": should be bind, hilite, or load"
699        }
700    }
701}
702
703# ----------------------------------------------------------------------
704# USAGE: _fixControls ?<eventArgs...>?
705#
706# Called automatically at the idle point after one or more results
707# have been added to this result set.  Scans through all existing
708# data and updates controls used to select the data.
709# ----------------------------------------------------------------------
710itcl::body Rappture::ResultSet::_fixControls {args} {
711    if {[$_results size] == 0} {
712        return
713    }
714
715    set popup [$itk_component(options) contents frame]
716    grid columnconfigure $popup 0 -minsize 16
717    grid columnconfigure $popup 1 -weight 1
718
719    set shortlist $itk_component(dials)
720    grid columnconfigure $shortlist 1 -weight 1
721
722    #
723    # Scan through all columns in the data and create any
724    # controls that just appeared.
725    #
726    set nadded 0
727    foreach col [$_results column names] {
728        set xmlobj [$_results get -format xmlobj 0]
729
730        #
731        # If this column doesn't have a control yet, then
732        # create one.
733        #
734        if {![info exists _cntlInfo($this-$col-id)]} {
735            set row [lindex [grid size $popup] 1]
736            set row2 [expr {$row+1}]
737
738            set tip ""
739            if {$col == "xmlobj"} {
740                set quantity "Simulation"
741                set tip "List of all simulations that you have performed so far."
742            } else {
743                # search for the first XML object with this element defined
744                foreach xmlobj [$_results get -format xmlobj] {
745                    set quantity [$xmlobj get $col.about.label]
746                    set tip [$xmlobj get $col.about.description]
747                    if {"" != $quantity} {
748                        break
749                    }
750                }
751                if {"" == $quantity && "" != $xmlobj} {
752                    set quantity [$xmlobj element -as id $col]
753                }
754            }
755
756            #
757            # Build the main control in the pop-up panel.
758            #
759            set fn $itk_option(-textfont)
760            set w $popup.label$_counter
761            frame $w
762            grid $w -row $row -column 2 -sticky ew -padx 4 -pady {4 0}
763            label $w.arrow -bitmap [Rappture::icon empty] -borderwidth 0
764            pack $w.arrow -side left -fill y
765            _control bind $w.arrow $col
766
767            label $w.name -text $quantity -anchor w \
768                -borderwidth 0 -padx 0 -pady 1 -font $fn
769            pack $w.name -side left
770            bind $w.name <Configure> [itcl::code $this _fixValue $col resize]
771            _control bind $w.name $col
772
773            label $w.value -anchor w \
774                -borderwidth 0 -padx 0 -pady 1 -font $fn
775            pack $w.value -side left
776            bind $w.value <Configure> [itcl::code $this _fixValue $col resize]
777            _control bind $w.value $col
778
779            Rappture::Tooltip::for $w \
780                "@[itcl::code $this _getTooltip label $col]"
781
782            set w $popup.dial$_counter
783            Rappture::Radiodial $w -valuewidth 0
784            grid $w -row $row2 -column 2 -sticky ew -padx 4 -pady {0 4}
785            $w configure -variable ::Rappture::ResultSet::_cntlInfo($this-$col-value)
786            Rappture::Tooltip::for $w \
787                "@[itcl::code $this _getTooltip dial $col]"
788
789            set w $popup.all$_counter
790            label $w -text "All" -padx 8 \
791                -borderwidth 1 -relief raised -font $fn
792            grid $w -row $row -rowspan 2 -column 1 -sticky nsew -padx 2 -pady 4
793            Rappture::Tooltip::for $w \
794                "@[itcl::code $this _getTooltip all $col]"
795            bind $w <ButtonRelease> [itcl::code $this _toggleAll $col]
796
797            # Create the controls for the "short list" area.
798            set w $shortlist.label$_counter
799            frame $w
800            grid $w -row $row -column 1 -sticky ew
801            label $w.arrow -bitmap [Rappture::icon empty] -borderwidth 0
802            pack $w.arrow -side left -fill y
803            _control bind $w.arrow $col
804
805            label $w.name -text $quantity -anchor w \
806                -borderwidth 0 -padx 0 -pady 1 -font $fn
807            pack $w.name -side left
808            bind $w.name <Configure> [itcl::code $this _fixValue $col resize]
809            _control bind $w.name $col
810
811            label $w.value -anchor w \
812                -borderwidth 0 -padx 0 -pady 1 -font $fn
813            pack $w.value -side left
814            bind $w.value <Configure> [itcl::code $this _fixValue $col resize]
815            _control bind $w.value $col
816
817            Rappture::Tooltip::for $w \
818                "@[itcl::code $this _getTooltip label $col]"
819
820            # if this is the "Simulation #" control, add a separator
821            if {$col == "xmlobj"} {
822                grid $popup.all$_counter -column 0
823                grid $popup.label$_counter -column 1 -columnspan 2
824                grid $popup.dial$_counter -column 1 -columnspan 2
825
826                if {![winfo exists $popup.sep]} {
827                    frame $popup.sep -height 1 -borderwidth 0 -background black
828                }
829                grid $popup.sep -row [expr {$row+2}] -column 0 \
830                    -columnspan 3 -sticky ew -pady 4
831
832                if {![winfo exists $popup.paraml]} {
833                    label $popup.paraml -text "Parameters:" -font $fn
834                }
835                grid $popup.paraml -row [expr {$row+3}] -column 0 \
836                    -columnspan 3 -sticky w -padx 4 -pady {0 4}
837            }
838
839            # create a record for this control
840            lappend _cntlInfo($this-all) $col
841            set _cntlInfo($this-$col-id) $_counter
842            set _cntlInfo($this-$col-label) $quantity
843            set _cntlInfo($this-$col-tip) $tip
844            set _cntlInfo($this-$col-value) ""
845            set _cntlInfo($this-$col-usage) 0
846            set _cntlInfo($this-$col) ""
847
848            trace add variable _cntlInfo($this-$col-value) write \
849                "[itcl::code $this _fixValue $col value]; list"
850
851            incr _counter
852
853            # fix the shortlist layout to show as many controls as we can
854            $_dispatcher event -now !layout why data
855
856            # let clients know that a new control appeared
857            # so they can fix the overall size accordingly
858            event generate $itk_component(hull) <<Control>>
859        }
860
861        #
862        # Determine the unique values for this column and load
863        # them into the control.
864        #
865        set id $_cntlInfo($this-$col-id)
866        set popup [$itk_component(options) contents frame]
867        set dial $popup.dial$id
868
869        _control load $popup.dial$id $col
870
871        if {$col == $_layout(active)} {
872            $shortlist.dial configure -variable ""
873            _control load $shortlist.dial $col
874            $shortlist.dial configure -variable \
875                "::Rappture::ResultSet::_cntlInfo($this-$col-value)"
876        }
877        incr nadded
878    }
879
880    #
881    # Activate the most recent control.  If a bunch of controls
882    # were just added, then activate the "Simulation" control,
883    # since that's the easiest way to page through results.
884    #
885    if {$nadded > 0} {
886        if {[$_results column names] == 2 || $nadded == 1} {
887            activate [lindex $_cntlInfo($this-all) end]
888        } else {
889            activate xmlobj
890        }
891    }
892
893    #
894    # Set all controls to the settings of the most recent addition.
895    # Setting the value slot will trigger the !settings event, which
896    # will then fix all other controls to match the one that changed.
897    #
898    if {"" != $_recent} {
899        set raw [lindex [$_results find -format xmlobj $_recent] 0]
900        set raw "#[expr {$raw+1}]"
901        set _cntlInfo($this-xmlobj-value) $raw
902    }
903}
904
905# ----------------------------------------------------------------------
906# USAGE: _fixLayout ?<eventArgs...>?
907#
908# Called automatically at the idle point after the controls have
909# changed, or the size of the window has changed.  Fixes the layout
910# so that the active control is displayed, and other recent controls
911# are shown above and/or below.  At the very least, we must show the
912# "more options..." control, which pops up a panel of all controls.
913# ----------------------------------------------------------------------
914itcl::body Rappture::ResultSet::_fixLayout {args} {
915    array set eventdata $args
916
917    set popup [$itk_component(options) contents frame]
918    set shortlist $itk_component(dials)
919
920    # clear out the short list area
921    foreach w [grid slaves $shortlist] {
922        grid forget $w
923    }
924
925    # reset all labels back to an ordinary font/background
926    set fn $itk_option(-textfont)
927    set bg $itk_option(-background)
928    set fg $itk_option(-foreground)
929    foreach col $_cntlInfo($this-all) {
930        set id $_cntlInfo($this-$col-id)
931        $popup.label$id configure -background $bg
932        $popup.label$id.arrow configure -background $bg \
933            -bitmap [Rappture::icon empty]
934        $popup.label$id.name configure -font $fn -background $bg
935        $popup.label$id.value configure -background $bg
936        $popup.all$id configure -background $bg -foreground $fg \
937            -relief raised
938        $popup.dial$id configure -background $bg
939        $shortlist.label$id configure -background $bg
940        $shortlist.label$id.arrow configure -background $bg \
941            -bitmap [Rappture::icon empty]
942        $shortlist.label$id.name configure -font $fn -background $bg
943        $shortlist.label$id.value configure -background $bg
944    }
945
946    # only 1 result? then we don't need any controls
947    if {[$_results size] < 2} {
948        return
949    }
950
951    # compute the number of controls that will fit in the shortlist area
952    set dials $itk_component(dials)
953    set h [winfo height $dials]
954    set normalLine [font metrics $itk_option(-textfont) -linespace]
955    set boldLine [font metrics $itk_option(-boldfont) -linespace]
956    set active [expr {$boldLine+[winfo reqheight $dials.dial]+4}]
957
958    if {$h < $active+$normalLine} {
959        # active control kinda big? then show parameter values only
960        set _layout(mode) abbreviated
961        set ncntls [expr {int(floor(double($h)/$normalLine))}]
962    } else {
963        set _layout(mode) usual
964        set ncntls [expr {int(floor(double($h-$active)/$normalLine))+1}]
965    }
966
967    # find the controls with the most usage
968    set order ""
969    foreach col $_cntlInfo($this-all) {
970        lappend order [list $col $_cntlInfo($this-$col-usage)]
971    }
972    set order [lsort -integer -decreasing -index 1 $order]
973
974    set mostUsed ""
975    if {[llength $order] <= $ncntls} {
976        # plenty of space? then show all controls
977        foreach item $order {
978            lappend mostUsed [lindex $item 0]
979        }
980    } else {
981        # otherwise, limit to the most-used controls
982        foreach item [lrange $order 0 [expr {$ncntls-1}]] {
983            lappend mostUsed [lindex $item 0]
984        }
985
986        # make sure the active control is included
987        if {"" != $_active && [lsearch -exact $mostUsed $_active] < 0} {
988            set mostUsed [lreplace [linsert $mostUsed 0 $_active] end end]
989        }
990
991        # if there are more controls, add the "more parameters..." entry
992        if {$ncntls > 2} {
993            set mostUsed [lreplace $mostUsed end end @more]
994            set rest [expr {[llength $order]-($ncntls-1)}]
995            if {$rest == 1} {
996                $dials.labelmore.name configure -text "1 more parameter..."
997            } else {
998                $dials.labelmore.name configure -text "$rest more parameters..."
999            }
1000        }
1001    }
1002
1003    # draw the active control
1004    set row 0
1005    foreach col [concat $_cntlInfo($this-all) @more] {
1006        # this control not on the short list? then ignore it
1007        if {[lsearch $mostUsed $col] < 0} {
1008            continue
1009        }
1010
1011        if {[string index $col 0] == "@"} {
1012            set id [string range $col 1 end]
1013        } else {
1014            set id $_cntlInfo($this-$col-id)
1015        }
1016        grid $shortlist.label$id -row $row -column 1 -sticky ew -padx 4
1017
1018        if {$col == $_active} {
1019            # put the background behind the active control in the popup
1020            set id $_cntlInfo($this-$_active-id)
1021            array set ginfo [grid info $popup.label$id]
1022            grid $popup.bg -row $ginfo(-row) -rowspan 2 \
1023                -column 0 -columnspan 3 -sticky nsew
1024            lower $popup.bg
1025
1026            if {$_layout(mode) == "usual"} {
1027                # put the background behind the active control in the shortlist
1028                grid $shortlist.bg -row $row -rowspan 2 \
1029                    -column 0 -columnspan 2 -sticky nsew
1030                lower $shortlist.bg
1031
1032                # place the All and dial in the shortlist area
1033                grid $shortlist.all -row $row -rowspan 2 -column 0 \
1034                    -sticky nsew -padx 2 -pady 2
1035                grid $shortlist.dial -row [expr {$row+1}] -column 1 \
1036                    -sticky ew -padx 4
1037                incr row
1038
1039                if {$_layout(active) != $_active} {
1040                    $shortlist.dial configure -variable ""
1041                    _control load $shortlist.dial $col
1042                    $shortlist.dial configure -variable \
1043                        "::Rappture::ResultSet::_cntlInfo($this-$col-value)"
1044                    set _layout(active) $_active
1045                }
1046            }
1047        }
1048        incr row
1049    }
1050
1051    # highlight the active control
1052    if {[info exists _cntlInfo($this-$_active-id)]} {
1053        set id $_cntlInfo($this-$_active-id)
1054        set bf $itk_option(-boldfont)
1055        set fg $itk_option(-activecontrolforeground)
1056        set bg $itk_option(-activecontrolbackground)
1057
1058        $popup.label$id configure -background $bg
1059        $popup.label$id.arrow configure -foreground $fg -background $bg \
1060            -bitmap [Rappture::icon rarrow]
1061        $popup.label$id.name configure -foreground $fg -background $bg \
1062            -font $bf
1063        $popup.label$id.value configure -foreground $fg -background $bg
1064        $popup.dial$id configure -background $bg
1065        $popup.bg configure -background $bg
1066
1067        if {$_plotall} {
1068            $popup.all$id configure -relief sunken \
1069                -background $itk_option(-togglebackground) \
1070                -foreground $itk_option(-toggleforeground)
1071        } else {
1072            $popup.all$id configure -relief raised \
1073                -background $itk_option(-activecontrolbackground) \
1074                -foreground $itk_option(-activecontrolforeground)
1075        }
1076
1077        if {$_layout(mode) == "usual"} {
1078            $shortlist.label$id configure -background $bg
1079            $shortlist.label$id.arrow configure -foreground $fg \
1080                -background $bg -bitmap [Rappture::icon rarrow]
1081            $shortlist.label$id.name configure -foreground $fg \
1082                -background $bg -font $bf
1083            $shortlist.label$id.value configure -foreground $fg \
1084                -background $bg
1085            $shortlist.dial configure -background $bg
1086            $shortlist.bg configure -background $bg
1087
1088            if {[$shortlist.all cget -relief] == "raised"} {
1089                $shortlist.all configure -foreground $fg -background $bg
1090            }
1091        }
1092    }
1093}
1094
1095# ----------------------------------------------------------------------
1096# USAGE: _fixSettings ?<eventArgs...>?
1097#
1098# Called automatically at the idle point after a control has changed
1099# to load new data into the plotting area at the top of this result
1100# set.  Extracts the current tuple of control values from the control
1101# area, then finds the corresponding data values.  Loads the data
1102# by invoking a -settingscommand callback with parameters that
1103# describe what data should be plotted.
1104# ----------------------------------------------------------------------
1105itcl::body Rappture::ResultSet::_fixSettings {args} {
1106    array set eventdata $args
1107    if {[info exists eventdata(column)]} {
1108        set changed $eventdata(column)
1109    } else {
1110        set changed ""
1111    }
1112    _doPrompt off
1113
1114    switch -- [$_results size] {
1115        0 {
1116            # no data? then do nothing
1117            return
1118        }
1119        1 {
1120            # only one data set? then plot it
1121            _doSettings [list 0 [list -width 2 -description [_getParamDesc]]]
1122            return
1123        }
1124    }
1125
1126    #
1127    # Find the selected run.  If the run setting changed, then
1128    # look at its current value.  Otherwise, search the results
1129    # for a tuple that matches the current settings.
1130    #
1131    if {$changed == "xmlobj"} {
1132        # value is "#2" -- skip # and adjust range starting from 0
1133        set irun [string range $_cntlInfo($this-xmlobj-value) 1 end]
1134        if {"" != $irun} { set irun [expr {$irun-1}] }
1135    } else {
1136        set format ""
1137        set tuple ""
1138        foreach col [lrange [$_results column names] 1 end] {
1139            lappend format $col
1140            lappend tuple $_cntlInfo($this-$col-value)
1141        }
1142        set irun [lindex [$_results find -format $format -- $tuple] 0]
1143
1144        if {"" == $irun && "" != $changed
1145             && $itk_option(-missingdata) == "skip"} {
1146            #
1147            # No data for these settings.  Try leaving the next
1148            # column open, then the next, and so forth, until
1149            # we find some data.
1150            #
1151            # allcols:  foo bar baz qux
1152            #               ^^^changed
1153            #
1154            # search:   baz qux foo
1155            #
1156            set val $_cntlInfo($this-$changed-value)
1157            set allcols [lrange [$_results column names] 1 end]
1158            set i [lsearch -exact $allcols $changed]
1159            set search [concat \
1160                [lrange $allcols [expr {$i+1}] end] \
1161                [lrange $allcols 0 [expr {$i-1}]] \
1162            ]
1163            set nsearch [llength $search]
1164
1165            for {set i 0} {$i < $nsearch} {incr i} {
1166                set format $changed
1167                set tuple [list $val]
1168                for {set j [expr {$i+1}]} {$j < $nsearch} {incr j} {
1169                    set col [lindex $search $j]
1170                    lappend format $col
1171                    lappend tuple $_cntlInfo($this-$col-value)
1172                }
1173                set irun [lindex [$_results find -format $format -- $tuple] 0]
1174                if {"" != $irun} {
1175                    break
1176                }
1177            }
1178        }
1179    }
1180
1181    #
1182    # If we found a particular run, then load its values into all
1183    # controls.
1184    #
1185    if {"" != $irun} {
1186        # stop reacting to value changes
1187        set _settings 1
1188
1189        set format [lrange [$_results column names] 1 end]
1190        if {[llength $format] == 1} {
1191            set data [$_results get -format $format $irun]
1192        } else {
1193            set data [lindex [$_results get -format $format $irun] 0]
1194        }
1195
1196        foreach col $format val $data {
1197            set _cntlInfo($this-$col-value) $val
1198        }
1199        set _cntlInfo($this-xmlobj-value) "#[expr {$irun+1}]"
1200
1201        # okay, react to value changes again
1202        set _settings 0
1203    }
1204
1205    #
1206    # Search for tuples matching the current setting and
1207    # plot them.
1208    #
1209    if {$_plotall && $_active == "xmlobj"} {
1210        set format ""
1211    } else {
1212        set format ""
1213        set tuple ""
1214        foreach col [lrange [$_results column names] 1 end] {
1215            if {!$_plotall || $col != $_active} {
1216                lappend format $col
1217                lappend tuple $_cntlInfo($this-$col-value)
1218            }
1219        }
1220    }
1221
1222    if {"" != $format} {
1223        set ilist [$_results find -format $format -- $tuple]
1224    } else {
1225        set ilist [$_results find]
1226    }
1227
1228    if {[llength $ilist] > 0} {
1229        # search for the result for these settings
1230        set format ""
1231        set tuple ""
1232        foreach col [lrange [$_results column names] 1 end] {
1233            lappend format $col
1234            lappend tuple $_cntlInfo($this-$col-value)
1235        }
1236        set icurr [$_results find -format $format -- $tuple]
1237
1238        # no data for these settings? prompt the user to simulate
1239        if {"" == $icurr} {
1240            _doPrompt on
1241        }
1242
1243        if {[llength $ilist] == 1} {
1244            # single result -- always use active color
1245            set i [lindex $ilist 0]
1246            set plist [list $i [list -width 2 -description [_getParamDesc $i]]]
1247        } else {
1248            #
1249            # Get the color for all points according to
1250            # the color spectrum.
1251            #
1252            set plist ""
1253            foreach i $ilist {
1254                if {$i == $icurr} {
1255                    lappend plist $i [list -width 3 -raise 1 \
1256                        -description [_getParamDesc $i]]
1257                } else {
1258                    lappend plist $i [list -brightness 0.7 -width 1 \
1259                        -description [_getParamDesc $i]]
1260                }
1261            }
1262        }
1263
1264        #
1265        # Load up the matching plots
1266        #
1267        _doSettings $plist
1268
1269    } elseif {$itk_option(-missingdata) == "prompt"} {
1270        # prompt the user to simulate these settings
1271        _doPrompt on
1272        _doSettings  ;# clear plotting area
1273
1274        # clear the current run selection -- there is no run for this
1275        set _settings 1
1276        set _cntlInfo($this-xmlobj-value) ""
1277        set _settings 0
1278    }
1279}
1280
1281# ----------------------------------------------------------------------
1282# USAGE: _fixExplore
1283#
1284# Called automatically whenever the user toggles the "Explore" button
1285# on the parameter popup.  Changes the -missingdata option back and
1286# forth, to allow for missing data or skip it.
1287# ----------------------------------------------------------------------
1288itcl::body Rappture::ResultSet::_fixExplore {} {
1289    if {$_explore} {
1290        configure -missingdata prompt
1291    } else {
1292        configure -missingdata skip
1293    }
1294}
1295
1296# ----------------------------------------------------------------------
1297# USAGE: _fixValue <columnName> <why>
1298#
1299# Called automatically whenver a value for a parameter dial changes.
1300# Updates the interface to display the new value.  The <why> is a
1301# reason for the change, which may be "resize" (draw old value in
1302# new size) or "value" (value changed).
1303# ----------------------------------------------------------------------
1304itcl::body Rappture::ResultSet::_fixValue {col why} {
1305    if {[info exists _cntlInfo($this-$col-id)]} {
1306        set id $_cntlInfo($this-$col-id)
1307
1308        set popup [$itk_component(options) contents frame]
1309        set widget $popup.label$id
1310        set wmax [winfo width $popup.dial$id]
1311        _drawValue $col $widget $wmax
1312
1313        set widget $itk_component(dials).label$id
1314        set wmax [winfo width $itk_component(dials).dial]
1315        if {$wmax <= 1} {
1316            set wmax [expr {round(0.9*[winfo width $itk_component(cntls)])}]
1317        }
1318        _drawValue $col $widget $wmax
1319
1320        if {$why == "value" && !$_settings} {
1321            # keep track of usage, so we know which controls are popular
1322            incr _cntlInfo($this-$col-usage)
1323
1324            # adjust the settings according to the value in the column
1325            $_dispatcher event -idle !settings column $col
1326        }
1327    }
1328}
1329
1330# ----------------------------------------------------------------------
1331# USAGE: _drawValue <columnName> <widget> <widthMax>
1332#
1333# Used internally to fix the rendering of a "quantity = value" display.
1334# If the name/value in <widget> are smaller than <widthMax>, then the
1335# full "quantity = value" string is displayed.  Otherwise, an
1336# abbreviated form is displayed.
1337# ----------------------------------------------------------------------
1338itcl::body Rappture::ResultSet::_drawValue {col widget wmax} {
1339    set quantity $_cntlInfo($this-$col-label)
1340    regsub -all {\n} $quantity " " quantity  ;# take out newlines
1341
1342    set newval $_cntlInfo($this-$col-value)
1343    regsub -all {\n} $newval " " newval  ;# take out newlines
1344
1345    set lfont [$widget.name cget -font]
1346    set vfont [$widget.value cget -font]
1347
1348    set wn [font measure $lfont $quantity]
1349    set wv [font measure $lfont " = $newval"]
1350    set w [expr {$wn + $wv}]
1351
1352    if {$w <= $wmax} {
1353        # if the text fits, then shown "quantity = value"
1354        $widget.name configure -text $quantity
1355        $widget.value configure -text " = $newval"
1356    } else {
1357        # Otherwise, we'll have to appreviate.
1358        # If the value is really long, then just show a little bit
1359        # of it.  Otherwise, show as much of the value as we can.
1360        if {[string length $newval] > 30} {
1361            set frac 0.8
1362        } else {
1363            set frac 0.2
1364        }
1365        set wNameSpace [expr {round($frac*$wmax)}]
1366        set wValueSpace [expr {$wmax-$wNameSpace}]
1367
1368        # fit as much of the "quantity" label in the space available
1369        if {$wn < $wNameSpace} {
1370            $widget.name configure -text $quantity
1371            set wValueSpace [expr {$wmax-$wn}]
1372        } else {
1373            set wDots [font measure $lfont "..."]
1374            set wchar [expr {double($wn)/[string length $quantity]}]
1375            while {1} {
1376                # figure out a good size for the abbreviated string
1377                set cmax [expr {round(($wNameSpace-$wDots)/$wchar)}]
1378                if {$cmax < 0} {set cmax 0}
1379                set str "[string range $quantity 0 $cmax]..."
1380                if {[font measure $lfont $str] <= $wNameSpace
1381                      || $wDots >= $wNameSpace} {
1382                    break
1383                }
1384                # we're measuring with average chars, so we may have
1385                # to shave a little off and do this again
1386                set wDots [expr {$wDots+2*$wchar}]
1387            }
1388            $widget.name configure -text $str
1389            set wValueSpace [expr {$wmax-[font measure $lfont $str]}]
1390        }
1391
1392        if {$wv < $wValueSpace} {
1393            $widget.value configure -text " = $newval"
1394        } else {
1395            set wDots [font measure $vfont "..."]
1396            set wEq [font measure $vfont " = "]
1397            set wchar [expr {double($wv)/[string length " = $newval"]}]
1398            while {1} {
1399                # figure out a good size for the abbreviated string
1400                set cmax [expr {round(($wValueSpace-$wDots-$wEq)/$wchar)}]
1401                if {$cmax < 0} {set cmax 0}
1402                set str " = [string range $newval 0 $cmax]..."
1403                if {[font measure $vfont $str] <= $wValueSpace
1404                      || $wDots >= $wValueSpace} {
1405                    break
1406                }
1407                # we're measuring with average chars, so we may have
1408                # to shave a little off and do this again
1409                set wDots [expr {$wDots+2*$wchar}]
1410            }
1411            $widget.value configure -text $str
1412        }
1413    }
1414}
1415
1416# ----------------------------------------------------------------------
1417# USAGE: _toggleAll ?<columnName>?
1418#
1419# Called automatically whenever the user clicks on an "All" button.
1420# Toggles the button between its on/off states.  In the "on" state,
1421# all results associated with the current control are sent to the
1422# result viewer.
1423# ----------------------------------------------------------------------
1424itcl::body Rappture::ResultSet::_toggleAll {{col "current"}} {
1425    if {$col == "current"} {
1426        set col $_active
1427    }
1428    if {![info exists _cntlInfo($this-$col-id)]} {
1429        return
1430    }
1431    set id $_cntlInfo($this-$col-id)
1432    set popup [$itk_component(options) contents frame]
1433    set pbutton $popup.all$id
1434    set current [$pbutton cget -relief]
1435    set sbutton $itk_component(dials).all
1436
1437    foreach c $_cntlInfo($this-all) {
1438        set id $_cntlInfo($this-$c-id)
1439        $popup.all$id configure -relief raised \
1440            -background $itk_option(-background) \
1441            -foreground $itk_option(-foreground)
1442    }
1443
1444    if {$current == "sunken"} {
1445        $pbutton configure -relief raised \
1446            -background $itk_option(-activecontrolbackground) \
1447            -foreground $itk_option(-activecontrolforeground)
1448        $sbutton configure -relief raised \
1449            -background $itk_option(-activecontrolbackground) \
1450            -foreground $itk_option(-activecontrolforeground)
1451        set _plotall 0
1452    } else {
1453        $pbutton configure -relief sunken \
1454            -background $itk_option(-togglebackground) \
1455            -foreground $itk_option(-toggleforeground)
1456        $sbutton configure -relief sunken \
1457            -background $itk_option(-togglebackground) \
1458            -foreground $itk_option(-toggleforeground)
1459        set _plotall 1
1460
1461        if {$col != $_active} {
1462            # clicked on an inactive "All" button? then activate that column
1463            activate $col
1464        }
1465    }
1466    $_dispatcher event -idle !settings
1467}
1468
1469# ----------------------------------------------------------------------
1470# USAGE: _getTooltip <role> <column>
1471#
1472# Called automatically whenever the user hovers a control within
1473# this widget.  Returns the tooltip associated with the control.
1474# ----------------------------------------------------------------------
1475itcl::body Rappture::ResultSet::_getTooltip {role column} {
1476    set label ""
1477    set tip ""
1478    if {$column == "active"} {
1479        set column $_active
1480    }
1481    if {[info exists _cntlInfo($this-$column-label)]} {
1482        set label $_cntlInfo($this-$column-label)
1483    }
1484    if {[info exists _cntlInfo($this-$column-tip)]} {
1485        set tip $_cntlInfo($this-$column-tip)
1486    }
1487
1488    switch -- $role {
1489        label {
1490            if {$column != $_active} {
1491                append tip "\n\nClick to activate this control."
1492            }
1493        }
1494        dial {
1495            append tip "\n\nClick to change the value of this parameter."
1496        }
1497        all {
1498            if {$label == ""} {
1499                set tip "Plot all values for this quantity."
1500            } else {
1501                set tip "Plot all values for $label."
1502            }
1503            if {$_plotall} {
1504                set what "all values"
1505            } else {
1506                set what "one value"
1507            }
1508            append tip "\n\nCurrently, plotting $what.  Click to toggle."
1509        }
1510        more {
1511            set tip "Click to access all parameters."
1512        }
1513    }
1514    return [string trim $tip]
1515}
1516
1517# ----------------------------------------------------------------------
1518# USAGE: _getParamDesc ?<index>?
1519#
1520# Used internally to build a descripton of parameters for the data
1521# tuple at the specified <index>.  This is passed on to the underlying
1522# results viewer, so it will know what data is being viewed.
1523# ----------------------------------------------------------------------
1524itcl::body Rappture::ResultSet::_getParamDesc {{index "current"}} {
1525    if {$index == "current"} {
1526        # search for the result for these settings
1527        set format ""
1528        set tuple ""
1529        foreach col [lrange [$_results column names] 1 end] {
1530            lappend format $col
1531            lappend tuple $_cntlInfo($this-$col-value)
1532        }
1533        set index [$_results find -format $format -- $tuple]
1534        if {"" == $index} {
1535            return ""  ;# somethings wrong -- bail out!
1536        }
1537    }
1538
1539    set desc ""
1540    foreach col $_cntlInfo($this-all) {
1541        set quantity $_cntlInfo($this-$col-label)
1542        set val [lindex [$_results get -format $col $index] 0]
1543        if {$col == "xmlobj"} {
1544            set num [lindex [$_results find -format xmlobj $val] 0]
1545            set val "#[expr {$num+1}]"
1546        }
1547        append desc "$quantity = $val\n"
1548    }
1549    return [string trim $desc]
1550}
1551
1552# ----------------------------------------------------------------------
1553# OPTION: -missingdata
1554# ----------------------------------------------------------------------
1555itcl::configbody Rappture::ResultSet::missingdata {
1556    set opts {prompt skip}
1557    if {[lsearch -exact $opts $itk_option(-missingdata)] < 0} {
1558        error "bad value \"$itk_option(-missingdata)\": should be [join $opts {, }]"
1559    }
1560    set _explore [expr {$itk_option(-missingdata) != "skip"}]
1561}
1562
1563# ----------------------------------------------------------------------
1564# OPTION: -activecontrolbackground
1565# ----------------------------------------------------------------------
1566itcl::configbody Rappture::ResultSet::activecontrolbackground {
1567    $_dispatcher event -idle !layout
1568}
1569
1570# ----------------------------------------------------------------------
1571# OPTION: -activecontrolforeground
1572# ----------------------------------------------------------------------
1573itcl::configbody Rappture::ResultSet::activecontrolforeground {
1574    $_dispatcher event -idle !layout
1575}
Note: See TracBrowser for help on using the repository browser.