Changeset 3024


Ignore:
Timestamp:
Jun 6, 2012 5:28:53 PM (12 years ago)
Author:
mmc
Message:

Major rewrite of the Analyzer and management of results so that "Clear
One Result" will work properly--even with "analyzer last" and "control
manual-resim". The old ResultSet? widget is now a ResultSelector? widget
that takes a ResultSet? as a data object. The ResultSet? contains all
Rappture::LibraryObj? result objects and computes diffs between results.
The ResultSet? owns all objects; when a result is cleared from the
ResultSet?, the object is deleted. The ResultSet? notifies various
clients (ResultSelector? and Analyzer) whenever there are changes, such
as adding or clearing a result.

Fixed the core (scew) library to avoid ignoring whitespace and trimming
strings. This was causing problems with string values and MIME-encoded
images with trailing newlines. The original (with newlines) seemed like
a different value than the version in the run.xml file (without newlines).
Some simulators may depend on the newlines. The Tcl library version
preserves the newlines, so the scew version should too.

Location:
trunk
Files:
1 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/gui/scripts/Makefile.in

    r2992 r3024  
    9595                $(srcdir)/resources.tcl \
    9696                $(srcdir)/resultset.tcl \
     97                $(srcdir)/resultselector.tcl \
    9798                $(srcdir)/resultviewer.tcl \
    9899                $(srcdir)/scroller.tcl \
  • trunk/gui/scripts/analyzer.tcl

    r2943 r3024  
    5555    public method reset {{when -eventually}}
    5656    public method load {xmlobj}
    57     public method clear {{xmlobj ""}}
     57    public method clear {{xmlobj "all"}}
    5858    public method download {option args}
    5959
     
    6262    protected method _autoLabel {xmlobj path title cntVar}
    6363    protected method _fixResult {}
     64    protected method _fixResultSet {args}
    6465    protected method _fixSize {}
    6566    protected method _fixSimControl {}
     
    7778    private variable _appName ""       ;# Name of application
    7879    private variable _control "manual" ;# start mode
    79     private variable _runs ""          ;# list of XML objects with results
     80    private variable _resultset ""     ;# ResultSet object with all results
    8081    private variable _pages 0          ;# number of pages for result sets
    8182    private variable _label2page       ;# maps output label => result set
     
    9798    set _tool $tool
    9899
     100    # use this to store all simulation results
     101    set _resultset [Rappture::ResultSet ::#auto]
     102    $_resultset notify add $this [itcl::code $this _fixResultSet]
     103
     104    # widget settings...
    99105    itk_option add hull.width hull.height
    100106    pack propagate $itk_component(hull) no
     
    258264    pack $w.top.l -side left
    259265
    260     itk_component add resultselector {
     266    itk_component add viewselector {
    261267        Rappture::Combobox $w.top.sel -width 10 -editable no
    262268    } {
     
    264270        rename -font -textfont textFont Font
    265271    }
    266     pack $itk_component(resultselector) -side left -expand yes -fill x
    267     bind $itk_component(resultselector) <<Value>> [itcl::code $this _fixResult]
    268     bind $itk_component(resultselector) <Enter> \
     272    pack $itk_component(viewselector) -side left -expand yes -fill x
     273    bind $itk_component(viewselector) <<Value>> [itcl::code $this _fixResult]
     274    bind $itk_component(viewselector) <Enter> \
    269275        [itcl::code $this download coming]
    270276
    271     Rappture::Tooltip::for $itk_component(resultselector) \
     277    Rappture::Tooltip::for $itk_component(viewselector) \
    272278        "@[itcl::code $this _resultTooltip]"
    273279
    274     $itk_component(resultselector) choices insert end \
     280    $itk_component(viewselector) choices insert end \
    275281        --- "---"
    276282
     
    284290        [itcl::code $this download coming]
    285291
    286     $itk_component(resultselector) choices insert end \
     292    $itk_component(viewselector) choices insert end \
    287293        @download [Rappture::filexfer::label download]
    288294
     
    307313
    308314    set f [$itk_component(results) insert end -fraction 0.1]
    309     itk_component add resultset {
    310         Rappture::ResultSet $f.rset \
    311             -clearcommand [itcl::code $this clear] \
    312             -settingscommand [itcl::code $this _plot] \
    313             -promptcommand [itcl::code $this _simState]
    314     }
    315     pack $itk_component(resultset) -expand yes -fill both
    316     bind $itk_component(resultset) <<Control>> [itcl::code $this _fixSize]
     315    itk_component add resultselector {
     316        Rappture::ResultSelector $f.rsel -resultset $_resultset \
     317            -settingscommand [itcl::code $this _plot]
     318    }
     319    pack $itk_component(resultselector) -expand yes -fill both
     320    bind $itk_component(resultselector) <<Layout>> [itcl::code $this _fixSize]
    317321    bind $itk_component(results) <Configure> [itcl::code $this _fixSize]
    318322
     
    358362# ----------------------------------------------------------------------
    359363itcl::body Rappture::Analyzer::destructor {} {
    360     foreach obj $_runs {
    361         itcl::delete object $obj
    362     }
    363364    after cancel [itcl::code $this simulate]
     365    itcl::delete object $_resultset
    364366}
    365367
     
    378380        # check to see if simulation is really needed
    379381        $_tool sync
    380         if {[$itk_component(resultset) contains [$_tool xml object]]
     382        if {[$_resultset contains [$_tool xml object]]
    381383              && ![string equal $_control "manual-resim"]} {
    382384            # not needed -- show results and return
     
    466468    # check to see if simulation is really needed
    467469    $_tool sync
    468     if {![$itk_component(resultset) contains [$_tool xml object]]
     470    if {![$_resultset contains [$_tool xml object]]
    469471          || [string equal $_control "manual-resim"]} {
    470472        # if control mode is "auto", then simulate right away
     
    523525    }
    524526
    525     lappend _runs $xmlobj
    526 
    527     # Detect molecule elements that contain trajectory data and convert
    528     # to sequences.
    529     _trajToSequence $xmlobj output
    530 
    531     # Go through the analysis and find all result sets.
    532     set haveresults 0
    533     foreach item [_reorder [$xmlobj children output]] {
    534         switch -glob -- $item {
    535             log* {
    536                 _autoLabel $xmlobj output.$item "Output Log" counters
    537             }
    538             number* {
    539                 _autoLabel $xmlobj output.$item "Number" counters
    540             }
    541             integer* {
    542                 _autoLabel $xmlobj output.$item "Integer" counters
    543             }
    544             string* {
    545                 _autoLabel $xmlobj output.$item "String" counters
    546             }
    547             histogram* - curve* - field* {
    548                 _autoLabel $xmlobj output.$item "Plot" counters
    549             }
    550             drawing* {
    551                 _autoLabel $xmlobj output.$item "Drawing" counters
    552             }
    553             structure* {
    554                 _autoLabel $xmlobj output.$item "Structure" counters
    555             }
    556             table* {
    557                 _autoLabel $xmlobj output.$item "Energy Levels" counters
    558             }
    559             sequence* {
    560                 _autoLabel $xmlobj output.$item "Sequence" counters
    561             }
    562             default {
    563                 if 0 {
    564                     puts stderr "unknown output $item"
    565                 }
    566             }
    567         }
    568         set label [$xmlobj get output.$item.about.group]
    569         if {"" == $label} {
    570             set label [$xmlobj get output.$item.about.label]
    571         }
    572 
    573         set hidden [$xmlobj get output.$item.hide]
    574         set hidden [expr {"" != $hidden && $hidden}]
    575 
    576         if {"" != $label && !$hidden} {
    577             set haveresults 1
    578         }
    579     }
    580     # if there are any valid results, add them to the resultset
    581     if {$haveresults} {
    582         set index [$itk_component(resultset) add $xmlobj]
    583 
    584         # add each result to a result viewer
    585         foreach item [_reorder [$xmlobj children output]] {
    586             set label [$xmlobj get output.$item.about.group]
    587             if {"" == $label} {
    588                 set label [$xmlobj get output.$item.about.label]
    589             }
    590             set hidden [$xmlobj get output.$item.hide]
    591             if { $hidden == "" } {
    592                 set hidden 0
    593             }
    594             if {"" != $label && !$hidden} {
    595                 if {![info exists _label2page($label)]} {
    596                     set name "page[incr _pages]"
    597                     set page [$itk_component(resultpages) insert end $name]
    598                     set _label2page($label) $page
    599                     set _label2desc($label) \
    600                         [$xmlobj get output.$item.about.description]
    601                     Rappture::ResultViewer $page.rviewer
    602                     pack $page.rviewer -expand yes -fill both -pady 4
    603 
    604                     set end [$itk_component(resultselector) \
    605                         choices index -value ---]
    606                     if {$end < 0} {
    607                         set end "end"
    608                     }
    609                     $itk_component(resultselector) choices insert $end \
    610                         $name $label
    611                 }
    612 
    613                 # add/replace the latest result into this viewer
    614                 set page $_label2page($label)
    615 
    616                 if {![info exists reset($page)]} {
    617                     $page.rviewer clear $index
    618                     set reset($page) 1
    619                 }
    620                 $page.rviewer add $index $xmlobj output.$item
    621             }
    622         }
    623     }
    624 
    625     # show the first page by default
    626     set max [$itk_component(resultselector) choices size]
    627     for {set i 0} {$i < $max} {incr i} {
    628         set first [$itk_component(resultselector) choices get -label $i]
    629         if {$first != ""} {
    630             set page [$itk_component(resultselector) choices get -value $i]
    631             set char [string index $page 0]
    632             if {$char != "@" && $char != "-"} {
    633                 $itk_component(resultpages) current $page
    634                 $itk_component(resultselector) value $first
    635                 set _lastlabel $first
    636                 break
    637             }
    638         }
    639     }
     527    $_resultset add $xmlobj
     528
     529    # NOTE: Adding will trigger a !change event on the ResultSet
     530    # object, which will trigger calls to _fixResultSet to add
     531    # the results to display.
    640532}
    641533
     
    647539# Otherwise, all results are cleared.
    648540# ----------------------------------------------------------------------
    649 itcl::body Rappture::Analyzer::clear {{xmlobj ""}} {
    650     if {$xmlobj ne ""} {
    651         set i [lsearch -exact $_runs $xmlobj]
    652         if {$i >= 0} {
    653             itcl::delete object $xmlobj
    654             set _runs [lreplace $_runs $i $i]
    655 
    656             # delete this result from all viewers
    657             foreach label [array names _label2page] {
    658                 set page $_label2page($label)
    659                 $page.rviewer clear $xmlobj
    660             }
    661         }
     541itcl::body Rappture::Analyzer::clear {{xmlobj "all"}} {
     542    if {$xmlobj eq "" || $xmlobj eq "all"} {
     543        $_resultset clear
    662544    } else {
    663         # clear everything
    664         foreach obj $_runs {
    665             itcl::delete object $obj
    666         }
    667         set _runs ""
    668     }
    669 
    670     if {[llength $_runs] == 0} {
    671         # reset the size of the controls area
    672         set ht [winfo height $itk_component(results)]
    673         set cntlht [$itk_component(resultset) size -controlarea]
    674         set frac [expr {double($cntlht)/$ht}]
    675         $itk_component(results) fraction end $frac
    676 
    677         foreach label [array names _label2page] {
    678             set page $_label2page($label)
    679             destroy $page.rviewer
    680         }
    681         $itk_component(resultselector) value ""
    682         $itk_component(resultselector) choices delete 0 end
    683         catch {unset _label2page}
    684         catch {unset _label2desc}
    685         set _plotlist ""
    686 
    687         $itk_component(resultselector) choices insert end --- "---"
    688         $itk_component(resultselector) choices insert end \
    689             @download [Rappture::filexfer::label download]
    690         set _lastlabel ""
    691     }
    692 
    693     #
    694     # HACK ALERT!!
    695     # The following statement should be in place, but it causes
    696     # vtk to dump core.  Leave it out until we can fix the core dump.
    697     # In the mean time, we leak memory...
    698     #
    699     #$itk_component(resultpages) delete -all
    700     #set _pages 0
    701 
    702     _simState on
    703     _fixSimControl
    704     reset
     545        $_resultset clear $xmlobj
     546    }
     547
     548    # NOTE: Clearing will trigger a !change event on the ResultSet
     549    # object, which will trigger calls to _fixResultSet to clean up
     550    # the results being displayed.
    705551}
    706552
     
    714560# ----------------------------------------------------------------------
    715561itcl::body Rappture::Analyzer::download {option args} {
    716     set title [$itk_component(resultselector) value]
    717     set page [$itk_component(resultselector) translate $title]
     562    set title [$itk_component(viewselector) value]
     563    set page [$itk_component(viewselector) translate $title]
    718564
    719565    switch -- $option {
     
    775621                set ext ""
    776622                set f [$itk_component(resultpages) page $page]
    777                 set item [$itk_component(resultselector) value]
     623                set item [$itk_component(viewselector) value]
    778624                set result [$f.rviewer download now $widget $_appName $item]
    779625                if { $result == "" } {
     
    811657#
    812658# Used internally to update the plot shown in the current result
    813 # viewer whenever the resultset settings have changed.  Causes the
     659# viewer whenever the resultselector settings have changed.  Causes the
    814660# desired results to show up on screen.
    815661# ----------------------------------------------------------------------
     
    817663    set _plotlist $args
    818664
    819     set page [$itk_component(resultselector) value]
    820     set page [$itk_component(resultselector) translate $page]
     665    set page [$itk_component(viewselector) value]
     666    set page [$itk_component(viewselector) translate $page]
    821667    if {"" != $page} {
    822668        set f [$itk_component(resultpages) page $page]
     
    897743# ----------------------------------------------------------------------
    898744itcl::body Rappture::Analyzer::_fixResult {} {
    899     set name [$itk_component(resultselector) value]
     745    set name [$itk_component(viewselector) value]
    900746    set page ""
    901747    if {"" != $name} {
    902         set page [$itk_component(resultselector) translate $name]
     748        set page [$itk_component(viewselector) translate $name]
    903749    }
    904750    if {$page == "@download"} {
    905751        # put the combobox back to its last value
    906         $itk_component(resultselector) component entry configure -state normal
    907         $itk_component(resultselector) component entry delete 0 end
    908         $itk_component(resultselector) component entry insert end $_lastlabel
    909         $itk_component(resultselector) component entry configure -state disabled
     752        $itk_component(viewselector) component entry configure -state normal
     753        $itk_component(viewselector) component entry delete 0 end
     754        $itk_component(viewselector) component entry insert end $_lastlabel
     755        $itk_component(viewselector) component entry configure -state disabled
    910756        # perform the actual download
    911757        download start $itk_component(download)
    912758    } elseif {$page == "---"} {
    913759        # put the combobox back to its last value
    914         $itk_component(resultselector) component entry configure -state normal
    915         $itk_component(resultselector) component entry delete 0 end
    916         $itk_component(resultselector) component entry insert end $_lastlabel
    917         $itk_component(resultselector) component entry configure -state disabled
     760        $itk_component(viewselector) component entry configure -state normal
     761        $itk_component(viewselector) component entry delete 0 end
     762        $itk_component(viewselector) component entry insert end $_lastlabel
     763        $itk_component(viewselector) component entry configure -state disabled
    918764    } elseif {$page != ""} {
    919765        set _lastlabel $name
     
    930776
    931777# ----------------------------------------------------------------------
     778# USAGE: _fixResultSet ?<eventData>...?
     779#
     780# Used internally to react to changes within the ResultSet.  When a
     781# result is added, a new result viewer is created for the object.
     782# When all results are cleared, the viewers are deleted.
     783# ----------------------------------------------------------------------
     784itcl::body Rappture::Analyzer::_fixResultSet {args} {
     785    array set eventData $args
     786    switch -- $eventData(op) {
     787        add {
     788            set xmlobj $eventData(what)
     789
     790            # Detect molecule elements that contain trajectory data
     791            # and convert to sequences.
     792            _trajToSequence $xmlobj output
     793
     794            # Go through the analysis and find all result sets.
     795            set haveresults 0
     796            foreach item [_reorder [$xmlobj children output]] {
     797                switch -glob -- $item {
     798                    log* {
     799                        _autoLabel $xmlobj output.$item "Output Log" counters
     800                    }
     801                    number* {
     802                        _autoLabel $xmlobj output.$item "Number" counters
     803                    }
     804                    integer* {
     805                        _autoLabel $xmlobj output.$item "Integer" counters
     806                    }
     807                    string* {
     808                        _autoLabel $xmlobj output.$item "String" counters
     809                    }
     810                    histogram* - curve* - field* {
     811                        _autoLabel $xmlobj output.$item "Plot" counters
     812                    }
     813                    drawing* {
     814                        _autoLabel $xmlobj output.$item "Drawing" counters
     815                    }
     816                    structure* {
     817                        _autoLabel $xmlobj output.$item "Structure" counters
     818                    }
     819                    table* {
     820                        _autoLabel $xmlobj output.$item "Energy Levels" counters
     821                    }
     822                    sequence* {
     823                        _autoLabel $xmlobj output.$item "Sequence" counters
     824                    }
     825                }
     826                set label [$xmlobj get output.$item.about.group]
     827                if {"" == $label} {
     828                    set label [$xmlobj get output.$item.about.label]
     829                }
     830
     831                set hidden [$xmlobj get output.$item.hide]
     832                set hidden [expr {"" != $hidden && $hidden}]
     833
     834                if {"" != $label && !$hidden} {
     835                    set haveresults 1
     836                }
     837            }
     838
     839            # if there are any valid results, add them to the resultset
     840            if {$haveresults} {
     841                set index [$_resultset get simnum $xmlobj]
     842
     843                # add each result to a result viewer
     844                foreach item [_reorder [$xmlobj children output]] {
     845                    set label [$xmlobj get output.$item.about.group]
     846                    if {"" == $label} {
     847                        set label [$xmlobj get output.$item.about.label]
     848                    }
     849                    set hidden [$xmlobj get output.$item.hide]
     850                    if { $hidden == "" } {
     851                        set hidden 0
     852                    }
     853                    if {"" != $label && !$hidden} {
     854                        if {![info exists _label2page($label)]} {
     855                            set name "page[incr _pages]"
     856                            set page [$itk_component(resultpages) \
     857                                insert end $name]
     858                            set _label2page($label) $page
     859                            set _label2desc($label) \
     860                                [$xmlobj get output.$item.about.description]
     861                            Rappture::ResultViewer $page.rviewer
     862                            pack $page.rviewer -expand yes -fill both -pady 4
     863
     864                            set end [$itk_component(viewselector) \
     865                                choices index -value ---]
     866                            if {$end < 0} {
     867                                set end "end"
     868                            }
     869                            $itk_component(viewselector) choices insert $end \
     870                                $name $label
     871                        }
     872
     873                        # add/replace the latest result into this viewer
     874                        set page $_label2page($label)
     875
     876                        if {![info exists reset($page)]} {
     877                            $page.rviewer clear $index
     878                            set reset($page) 1
     879                        }
     880                        $page.rviewer add $index $xmlobj output.$item
     881                    }
     882                }
     883            }
     884
     885            # show the first page by default
     886            set max [$itk_component(viewselector) choices size]
     887            for {set i 0} {$i < $max} {incr i} {
     888                set first [$itk_component(viewselector) choices get -label $i]
     889                if {$first != ""} {
     890                    set page [$itk_component(viewselector) choices get -value $i]
     891                    set char [string index $page 0]
     892                    if {$char != "@" && $char != "-"} {
     893                        $itk_component(resultpages) current $page
     894                        $itk_component(viewselector) value $first
     895                        set _lastlabel $first
     896                        break
     897                    }
     898                }
     899            }
     900        }
     901        clear {
     902            set xmlobj $eventData(what)
     903            if {$xmlobj ne "all"} {
     904                # delete this result from all viewers
     905                foreach label [array names _label2page] {
     906                    set page $_label2page($label)
     907                    $page.rviewer clear $xmlobj
     908                }
     909            }
     910
     911            if {[$_resultset size] == 0} {
     912                # reset the size of the controls area
     913                set ht [winfo height $itk_component(results)]
     914                set cntlht [$itk_component(resultselector) size -controlarea]
     915                set frac [expr {double($cntlht)/$ht}]
     916                $itk_component(results) fraction end $frac
     917
     918                foreach label [array names _label2page] {
     919                    set page $_label2page($label)
     920                    destroy $page.rviewer
     921                }
     922                $itk_component(resultpages) delete -all
     923                set _pages 0
     924
     925                $itk_component(viewselector) value ""
     926                $itk_component(viewselector) choices delete 0 end
     927                catch {unset _label2page}
     928                catch {unset _label2desc}
     929                set _plotlist ""
     930
     931                $itk_component(viewselector) choices insert end --- "---"
     932                $itk_component(viewselector) choices insert end \
     933                    @download [Rappture::filexfer::label download]
     934                set _lastlabel ""
     935            }
     936
     937            # fix Simulate button state
     938            reset
     939        }
     940        default {
     941            error "don't know how to handle op \"$eventData(op)\""
     942        }
     943    }
     944}
     945
     946# ----------------------------------------------------------------------
    932947# USAGE: _fixSize
    933948#
     
    939954    set ht [winfo height $itk_component(results)]
    940955    if {$ht <= 1} { set ht [winfo reqheight $itk_component(results)] }
    941     set cntlht [$itk_component(resultset) size -controlarea]
     956    set cntlht [$itk_component(resultselector) size -controlarea]
    942957    set frac [expr {double($cntlht)/$ht}]
    943958
     
    11101125itcl::body Rappture::Analyzer::_resultTooltip {} {
    11111126    set tip ""
    1112     set name [$itk_component(resultselector) value]
     1127    set name [$itk_component(viewselector) value]
    11131128    if {[info exists _label2desc($name)] &&
    11141129         [string length $_label2desc($name)] > 0} {
     
    11421157            # with no data, requiring simulation.
    11431158            #
    1144             if {[$itk_component(resultset) size -controls] >= 2} {
     1159            if {[$itk_component(resultselector) size -controls] >= 2} {
    11451160                pack $itk_interior.simol -fill x -before $itk_interior.nb
    11461161            } else {
  • trunk/gui/scripts/main.tcl

    r2977 r3024  
    5555option add *Balloon*Radiobutton.font -*-helvetica-medium-r-normal-*-12-*
    5656option add *Balloon*Checkbutton.font -*-helvetica-medium-r-normal-*-12-*
    57 option add *ResultSet.controlbarBackground #6666cc
    58 option add *ResultSet.controlbarForeground white
    59 option add *ResultSet.activeControlBackground #ccccff
    60 option add *ResultSet.activeControlForeground black
     57option add *ResultSelector.controlbarBackground #6666cc
     58option add *ResultSelector.controlbarForeground white
     59option add *ResultSelector.activeControlBackground #ccccff
     60option add *ResultSelector.activeControlForeground black
    6161option add *Radiodial.length 3i
    6262option add *BugReport*banner*foreground white
  • trunk/gui/scripts/radiodial.tcl

    r1929 r3024  
    565565        }
    566566    }
    567     error "bad value \"$str\": should be something matching the raw values \"[join $_values ,]\""
     567
     568    # didn't match -- build a return string of possible values
     569    set labels ""
     570    foreach vlist $_values {
     571        foreach v $vlist {
     572            lappend labels "\"$_val2label($v)\""
     573        }
     574    }
     575    error "bad value \"$str\": should be one of [join $labels ,]"
    568576}
    569577
  • trunk/gui/scripts/resultset.tcl

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

    r3008 r3024  
    772772            return [list $raw $val]
    773773        }
    774         image {
    775             set raw ""
    776             if {[$libobj element $path.current] ne ""} {
    777                 set raw [$libobj get $path.current]
    778             } elseif {[$libobj element $path.default] ne ""} {
    779                 set raw [$libobj get $path.default]
    780             }
    781             set val [string trim $raw]  ;# spaces don't matter in image data
    782             return [list $raw $val]
    783         }
    784774    }
    785775
  • trunk/src/core/RpLibrary.cc

    r2408 r3024  
    6363        parser = scew_parser_create();
    6464
    65         scew_parser_ignore_whitespaces(parser, 1);
     65        // Don't ignore whitespaces!
     66        // Things like string inputs may have trailing newlines that
     67        // matter to the underlying application.
     68        scew_parser_ignore_whitespaces(parser, 0);
    6669
    6770        /* Loads an XML file */
Note: See TracChangeset for help on using the changeset viewer.