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

Last change on this file since 1394 was 1342, checked in by gah, 15 years ago

preliminary HQ output from molvisviewer; unexpand tabs; all jpeg generation at 100%

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