source: trunk/gui/scripts/analyzer.tcl @ 3078

Last change on this file since 3078 was 3078, checked in by mmc, 12 years ago

Fixed a problem with "clear one" where old results weren't getting cleared
from the results viewer, so number plots were trying to access data that
didn't exist anymore. Results are now being cleared properly.

File size: 52.2 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: analyzer - output area for Rappture
3#
4#  This widget acts as the output side of a Rappture application.
5#  When the input has changed, it displays a Simulate button that
6#  launches the simulation.  When a simulation is running, this
7#  area shows status.  When it is finished, the results appear
8#  in place of the button, according to the specs in the <analyze>
9#  XML data.
10# ======================================================================
11#  AUTHOR:  Michael McLennan, Purdue University
12#  Copyright (c) 2004-2005  Purdue Research Foundation
13#
14#  See the file "license.terms" for information on usage and
15#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
16# ======================================================================
17package require Itk
18
19option add *Analyzer.width 3.5i widgetDefault
20option add *Analyzer.height 4i widgetDefault
21option add *Analyzer.simControl "auto" widgetDefault
22option add *Analyzer.simControlBackground "" widgetDefault
23option add *Analyzer.simControlOutline gray widgetDefault
24option add *Analyzer.simControlActiveBackground #ffffcc widgetDefault
25option add *Analyzer.simControlActiveOutline black widgetDefault
26option add *Analyzer.notebookpage "about" widgetDefault
27
28option add *Analyzer.font \
29    -*-helvetica-medium-r-normal-*-12-* widgetDefault
30option add *Analyzer.codeFont \
31    -*-courier-medium-r-normal-*-12-* widgetDefault
32option add *Analyzer.textFont \
33    -*-helvetica-medium-r-normal-*-12-* widgetDefault
34option add *Analyzer.boldTextFont \
35    -*-helvetica-bold-r-normal-*-12-* widgetDefault
36
37itcl::class Rappture::Analyzer {
38    inherit itk::Widget
39
40    itk_option define -codefont codeFont Font ""
41    itk_option define -textfont textFont Font ""
42    itk_option define -boldtextfont boldTextFont Font ""
43    itk_option define -simcontrol simControl SimControl ""
44    itk_option define -simcontroloutline simControlOutline Background ""
45    itk_option define -simcontrolbackground simControlBackground Background ""
46    itk_option define -simcontrolactiveoutline simControlActiveOutline Background ""
47    itk_option define -simcontrolactivebackground simControlActiveBackground Background ""
48    itk_option define -holdwindow holdWindow HoldWindow ""
49    itk_option define -notebookpage notebookPage NotebookPage ""
50
51    constructor {tool args} { # defined below }
52    destructor { # defined below }
53
54    public method simulate {args}
55    public method reset {{when -eventually}}
56    public method load {xmlobj}
57    public method clear {{xmlobj "all"}}
58    public method download {option args}
59
60    protected method _plot {args}
61    protected method _reorder {comps}
62    protected method _autoLabel {xmlobj path title cntVar}
63    protected method _fixResult {}
64    protected method _fixResultSet {args}
65    protected method _fixSize {}
66    protected method _fixSimControl {}
67    protected method _fixNotebook {}
68    protected method _simState {state args}
69    protected method _simOutput {message}
70    protected method _resultTooltip {}
71    protected method _isPdbTrajectory {data}
72    protected method _isLammpsTrajectory {data}
73    protected method _pdbToSequence {xmlobj path id child data}
74    protected method _lammpsToSequence {xmlobj path id child data}
75    protected method _trajToSequence {xmlobj {path ""}}
76
77    private variable _tool ""          ;# belongs to this tool
78    private variable _appName ""       ;# Name of application
79    private variable _control "manual" ;# start mode
80    private variable _resultset ""     ;# ResultSet object with all results
81    private variable _pages 0          ;# number of pages for result sets
82    private variable _label2page       ;# maps output label => result set
83    private variable _label2desc       ;# maps output label => description
84    private variable _lastlabel ""     ;# label of last example loaded
85    private variable _plotlist ""      ;# items currently being plotted
86
87    private common job                 ;# array var used for blt::bgexec jobs
88}
89
90itk::usual Analyzer {
91    keep -background -cursor foreground -font
92}
93
94# ----------------------------------------------------------------------
95# CONSTRUCTOR
96# ----------------------------------------------------------------------
97itcl::body Rappture::Analyzer::constructor {tool args} {
98    set _tool $tool
99
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...
105    itk_option add hull.width hull.height
106    pack propagate $itk_component(hull) no
107
108    frame $itk_interior.simol -borderwidth 1 -relief flat
109    pack $itk_interior.simol -fill x
110
111    itk_component add simbg {
112        frame $itk_interior.simol.simbg -borderwidth 0
113    } {
114        usual
115        rename -background -simcontrolcolor simControlColor Color
116    }
117    pack $itk_component(simbg) -expand yes -fill both
118
119    set simtxt [$tool xml get tool.action.label]
120    if {"" == $simtxt} {
121        set simtxt "Simulate"
122    }
123    itk_component add simulate {
124        button $itk_component(simbg).simulate -text $simtxt \
125            -command [itcl::code $this simulate]
126    } {
127        usual
128        rename -highlightbackground -simcontrolcolor simControlColor Color
129    }
130    pack $itk_component(simulate) -side left -padx 4 -pady 4
131
132    # if there's a hub url, then add "About" and "Questions" links
133    set _appName [$_tool xml get tool.id]
134    set url [Rappture::Tool::resources -huburl]
135    if {"" != $url && "" != $_appName} {
136        itk_component add hubcntls {
137            frame $itk_component(simbg).hubcntls
138        } {
139            usual
140            rename -background -simcontrolcolor simControlColor Color
141        }
142        pack $itk_component(hubcntls) -side right -padx 4
143
144        itk_component add icon {
145            label $itk_component(hubcntls).icon -image [Rappture::icon ask] \
146                -highlightthickness 0
147        } {
148            usual
149            ignore -highlightthickness
150            rename -background -simcontrolcolor simControlColor Color
151        }
152        pack $itk_component(icon) -side left
153
154        itk_component add about {
155            button $itk_component(hubcntls).about -text "About this tool" \
156                -command [list Rappture::filexfer::webpage \
157                              "$url/tools/$_appName"]
158        } {
159            usual
160            ignore -font
161            rename -background -simcontrolcolor simControlColor Color
162            rename -highlightbackground -simcontrolcolor simControlColor Color
163        }
164        pack $itk_component(about) -side top -anchor w
165
166        itk_component add questions {
167            button $itk_component(hubcntls).questions -text Questions? \
168                -command [list Rappture::filexfer::webpage \
169                              "$url/resources/$_appName/questions"]
170        } {
171            usual
172            ignore -font
173            rename -background -simcontrolcolor simControlColor Color
174            rename -highlightbackground -simcontrolcolor simControlColor Color
175        }
176        pack $itk_component(questions) -side top -anchor w
177    }
178
179    itk_component add simstatus {
180        text $itk_component(simbg).simstatus -borderwidth 0 \
181            -highlightthickness 0 -height 1 -width 1 -wrap none \
182            -state disabled
183    } {
184        usual
185        ignore -highlightthickness
186        rename -background -simcontrolcolor simControlColor Color
187        rename -font -textfont textFont Font
188    }
189    pack $itk_component(simstatus) -side left -expand yes -fill x
190
191    $itk_component(simstatus) tag configure popup \
192        -underline 1 -foreground blue
193
194    $itk_component(simstatus) tag bind popup \
195        <Enter> {%W configure -cursor center_ptr}
196    $itk_component(simstatus) tag bind popup \
197        <Leave> {%W configure -cursor ""}
198    $itk_component(simstatus) tag bind popup \
199        <ButtonPress> {after idle {Rappture::Tooltip::tooltip show %W}}
200
201
202    itk_component add notebook {
203        Rappture::Notebook $itk_interior.nb
204    }
205    pack $itk_interior.nb -expand yes -fill both
206
207    # ------------------------------------------------------------------
208    # ABOUT PAGE
209    # ------------------------------------------------------------------
210    set w [$itk_component(notebook) insert end about]
211
212    Rappture::Scroller $w.info -xscrollmode off -yscrollmode auto
213    pack $w.info -expand yes -fill both -padx 4 -pady 20
214    itk_component add toolinfo {
215        text $w.info.text -width 1 -height 1 -wrap word \
216            -borderwidth 0 -highlightthickness 0
217    } {
218        usual
219        ignore -borderwidth -relief
220        rename -font -textfont textFont Font
221    }
222    $w.info contents $w.info.text
223
224    # ------------------------------------------------------------------
225    # SIMULATION PAGE
226    # ------------------------------------------------------------------
227    set w [$itk_component(notebook) insert end simulate]
228    frame $w.cntls
229    pack $w.cntls -side bottom -fill x -pady 12
230    frame $w.cntls.sep -background black -height 1
231    pack $w.cntls.sep -side top -fill x
232
233    itk_component add abort {
234        button $w.cntls.abort -text "Abort" \
235            -command [itcl::code $_tool abort]
236    }
237    pack $itk_component(abort) -side left -expand yes -padx 4 -pady 4
238
239    Rappture::Scroller $w.info -xscrollmode auto -yscrollmode auto
240    pack $w.info -expand yes -fill both -padx 4 -pady 4
241    itk_component add runinfo {
242        text $w.info.text -width 1 -height 1 -wrap none \
243            -borderwidth 0 -highlightthickness 0 \
244            -state disabled
245    } {
246        usual
247        ignore -borderwidth -relief
248        rename -font -codefont codeFont Font
249    }
250    $w.info contents $w.info.text
251
252    itk_component add progress {
253        Rappture::Progress $w.progress
254    }
255
256    # ------------------------------------------------------------------
257    # ANALYZE PAGE
258    # ------------------------------------------------------------------
259    set w [$itk_component(notebook) insert end analyze]
260
261    frame $w.top
262    pack $w.top -side top -fill x -pady 8
263    label $w.top.l -text "Result:" -font $itk_option(-font)
264    pack $w.top.l -side left
265
266    itk_component add viewselector {
267        Rappture::Combobox $w.top.sel -width 10 -editable no
268    } {
269        usual
270        rename -font -textfont textFont Font
271    }
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> \
275        [itcl::code $this download coming]
276
277    Rappture::Tooltip::for $itk_component(viewselector) \
278        "@[itcl::code $this _resultTooltip]"
279
280    $itk_component(viewselector) choices insert end \
281        --- "---"
282
283    itk_component add download {
284        button $w.top.dl -image [Rappture::icon download] -anchor e \
285            -borderwidth 1 -relief flat -overrelief raised \
286            -command [itcl::code $this download start $w.top.dl]
287    }
288    pack $itk_component(download) -side right -padx {4 0}
289    bind $itk_component(download) <Enter> \
290        [itcl::code $this download coming]
291
292    $itk_component(viewselector) choices insert end \
293        @download [Rappture::filexfer::label download]
294
295    if {[Rappture::filexfer::enabled]} {
296        Rappture::Tooltip::for $itk_component(download) "Downloads the current result to a new web browser window on your desktop.  From there, you can easily print or save results.
297
298NOTE:  Your web browser must allow pop-ups from this site.  If your output does not appear, look for a 'pop-up blocked' message and enable pop-ups."
299    } else {
300        Rappture::Tooltip::for $itk_component(download) "Saves the current result to a file on your desktop."
301    }
302
303    itk_component add results {
304        Rappture::Panes $w.pane -sashwidth 1 -sashrelief solid -sashpadding {4 0}
305    }
306    pack $itk_component(results) -expand yes -fill both
307    set f [$itk_component(results) pane 0]
308
309    itk_component add resultpages {
310        Rappture::Notebook $f.nb
311    }
312    pack $itk_component(resultpages) -expand yes -fill both
313
314    set f [$itk_component(results) insert end -fraction 0.1]
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]
321    bind $itk_component(results) <Configure> [itcl::code $this _fixSize]
322
323    eval itk_initialize $args
324
325    $itk_component(runinfo) tag configure ERROR -foreground red
326    $itk_component(runinfo) tag configure text -font $itk_option(-textfont)
327
328    #
329    # Load up tool info on the first page.
330    #
331    $itk_component(toolinfo) tag configure title \
332        -font $itk_option(-boldtextfont)
333
334    set mesg [$tool xml get tool.title]
335    if {"" != $mesg} {
336        $itk_component(toolinfo) insert end $mesg title
337        $itk_component(toolinfo) insert end "\n\n"
338    }
339
340    set mesg [$tool xml get tool.about]
341    if {"" != $mesg} {
342        $itk_component(toolinfo) insert end $mesg
343    }
344    $itk_component(toolinfo) configure -state disabled
345    $itk_component(notebook) current about
346
347    # tool can run on "manual" (default) or "auto"
348    set cntl [$tool xml get tool.control]
349    if {"" == $cntl} {
350        set cntl [$tool xml get tool.control.type]
351    }
352    if {"" != $cntl} {
353        set _control $cntl
354    }
355
356    # reset everything to a clean state
357    reset
358}
359
360# ----------------------------------------------------------------------
361# DESTRUCTOR
362# ----------------------------------------------------------------------
363itcl::body Rappture::Analyzer::destructor {} {
364    after cancel [itcl::code $this simulate]
365    $_resultset notify remove $this
366    itcl::delete object $_resultset
367}
368
369# ----------------------------------------------------------------------
370# USAGE: simulate ?-ifneeded?
371# USAGE: simulate ?<path1> <value1> <path2> <value2> ...?
372#
373# Kicks off the simulator by executing the tool.command associated
374# with the tool.  If any arguments are specified, they are used to
375# set parameters for the simulation.  While the simulation is running,
376# it shows status.  When the simulation is finished, it switches
377# automatically to "analyze" mode and shows the results.
378# ----------------------------------------------------------------------
379itcl::body Rappture::Analyzer::simulate {args} {
380    if {$args == "-ifneeded"} {
381        # check to see if simulation is really needed
382        $_tool sync
383        if {[$_resultset contains [$_tool xml object]]
384              && ![string equal $_control "manual-resim"]} {
385            # not needed -- show results and return
386            $itk_component(notebook) current analyze
387            return
388        }
389        set args ""
390    }
391
392    # simulation is needed -- go to simulation page
393    $itk_component(notebook) current simulate
394
395    # no progress messages yet
396    pack forget $itk_component(progress)
397    lappend args -output [itcl::code $this _simOutput]
398
399    _simState off
400    $itk_component(runinfo) configure -state normal
401    $itk_component(runinfo) delete 1.0 end
402    $itk_component(runinfo) insert end "Running simulation...\n\n" text
403    $itk_component(runinfo) configure -state disabled
404
405    # if the hold window is set, then put up a busy cursor
406    if {$itk_option(-holdwindow) != ""} {
407        blt::busy hold $itk_option(-holdwindow)
408        raise $itk_component(hull)
409    }
410
411    # execute the job
412    foreach {status result} [eval $_tool run $args] break
413
414    # if job was aborted, then allow simulation again
415    if {$result == "ABORT"} {
416        _simState on "Aborted"
417    }
418
419    # load results from run.xml into analyzer
420    if {$status == 0 && $result != "ABORT"} {
421        set status [catch {load $result} result]
422    }
423
424    # back to normal
425    if {$itk_option(-holdwindow) != ""} {
426        blt::busy release $itk_option(-holdwindow)
427    }
428    $itk_component(abort) configure -state disabled
429
430    if {$status != 0} {
431        $itk_component(runinfo) configure -state normal
432        $itk_component(runinfo) delete 1.0 end
433        $itk_component(runinfo) insert end "Problem launching job:\n\n" text
434        _simOutput $result
435        $itk_component(runinfo) configure -state disabled
436        $itk_component(runinfo) see 1.0
437
438        # Try to create a support ticket for this error.
439        # It may be a real problem.
440        if {[Rappture::bugreport::shouldReport for jobs]} {
441            set ::errorInfo "\n\n== RAPPTURE INPUT ==\n[$_tool xml xml]"
442            Rappture::bugreport::register "Problem launching job:\n$result"
443            Rappture::bugreport::attachment [$_tool xml xml]
444            Rappture::bugreport::send
445        }
446    } else {
447        $itk_component(notebook) current analyze
448    }
449
450    # do this last -- after _simOutput above
451    pack forget $itk_component(progress)
452}
453
454# ----------------------------------------------------------------------
455# USAGE: reset ?-eventually|-now?
456#
457# Used to reset the analyzer whenever the input to a simulation has
458# changed.  Sets the mode back to "simulate", so the user has to
459# simulate again to see the output.  If the <start> option is set
460# to "auto", the simulation is invoked immediately.
461# ----------------------------------------------------------------------
462itcl::body Rappture::Analyzer::reset {{when -eventually}} {
463    if {$when == "-eventually"} {
464        after cancel [list catch [itcl::code $this reset -now]]
465        after idle [list catch [itcl::code $this reset -now]]
466        return
467    }
468
469    # check to see if simulation is really needed
470    $_tool sync
471    if {![$_resultset contains [$_tool xml object]]
472          || [string equal $_control "manual-resim"]} {
473        # if control mode is "auto", then simulate right away
474        if {[string match auto* $_control]} {
475            # auto control -- don't need button
476            pack forget $itk_interior.simol
477
478            after cancel [itcl::code $this simulate]
479            after idle [itcl::code $this simulate]
480        } else {
481            _simState on "new input parameters"
482        }
483    } else {
484        _simState off
485    }
486}
487
488# ----------------------------------------------------------------------
489# USAGE: load <xmlobj>
490#
491# Loads the data from the given <xmlobj> into the appropriate results
492# sets.  If necessary, new results sets are created to store the data.
493# ----------------------------------------------------------------------
494itcl::body Rappture::Analyzer::load {xmlobj} {
495    # only show the last result? then clear first
496    if {[$_tool xml get tool.analyzer] == "last"} {
497        clear
498    }
499
500    $_resultset add $xmlobj
501
502    # NOTE: Adding will trigger a !change event on the ResultSet
503    # object, which will trigger calls to _fixResultSet to add
504    # the results to display.
505}
506
507# ----------------------------------------------------------------------
508# USAGE: clear ?<xmlobj>?
509#
510# Discards one or more results previously loaded into the analyzer.
511# If an <xmlobj> is specified, then that one result is cleared.
512# Otherwise, all results are cleared.
513# ----------------------------------------------------------------------
514itcl::body Rappture::Analyzer::clear {{xmlobj "all"}} {
515    if {$xmlobj eq "" || $xmlobj eq "all"} {
516        $_resultset clear
517    } else {
518        $_resultset clear $xmlobj
519    }
520
521    # NOTE: Clearing will trigger a !change event on the ResultSet
522    # object, which will trigger calls to _fixResultSet to clean up
523    # the results being displayed.
524}
525
526# ----------------------------------------------------------------------
527# USAGE: download coming
528# USAGE: download controls <downloadCommand>
529# USAGE: download start ?widget?
530# USAGE: download now ?widget?
531#
532# Spools the current result so the user can download it.
533# ----------------------------------------------------------------------
534itcl::body Rappture::Analyzer::download {option args} {
535    set title [$itk_component(viewselector) value]
536    set page [$itk_component(viewselector) translate $title]
537
538    switch -- $option {
539        coming {
540            #
541            # Warn result that a download is coming, in case
542            # it needs to take a screen snap.
543            #
544            if {![regexp {^(|@download|---)$} $page]} {
545                set f [$itk_component(resultpages) page $page]
546                $f.rviewer download coming
547            }
548        }
549        controls {
550            # no controls for this download yet
551            return ""
552        }
553        start {
554            set widget $itk_component(download)
555            if {[llength $args] > 0} {
556                set widget [lindex $args 0]
557                if {[catch {winfo class $widget}]} {
558                    set widget $itk_component(download)
559                }
560            }
561            #
562            # See if this download has any controls.  If so, then
563            # post them now and let the user continue the download
564            # after selecting a file format.
565            #
566            if {$page != ""} {
567                set ext ""
568                set f [$itk_component(resultpages) page $page]
569                set arg [itcl::code $this download now $widget]
570                set popup [$f.rviewer download controls $arg]
571                if {"" != $popup} {
572                    $popup activate $widget below
573                } else {
574                    download now $widget
575                }
576            } else {
577                # this shouldn't happen
578                set file error.html
579                set data "<h1>Not Found</h1>There is no result selected."
580            }
581        }
582        now {
583            set widget $itk_component(download)
584            if {[llength $args] > 0} {
585                set widget [lindex $args 0]
586                if {[catch {winfo class $widget}]} {
587                    set widget $itk_component(download)
588                }
589            }
590            #
591            # Perform the actual download.
592            #
593            if {$page != ""} {
594                set ext ""
595                set f [$itk_component(resultpages) page $page]
596                set item [$itk_component(viewselector) value]
597                set result [$f.rviewer download now $widget $_appName $item]
598                if { $result == "" } {
599                    return;                # User cancelled the download.
600                }
601                foreach {ext data} $result break
602                if {"" == $ext} {
603                    if {"" != $widget} {
604                        Rappture::Tooltip::cue $widget \
605                            "Can't download this result."
606                    }
607                    return
608                }
609                regsub -all {[\ -\/\:-\@\{-\~]} $title {} title
610                set file "$title$ext"
611            } else {
612                # this shouldn't happen
613                set file error.html
614                set data "<h1>Not Found</h1>There is no result selected."
615            }
616
617            set mesg [Rappture::filexfer::download $data $file]
618            if {[string length $mesg] > 0} {
619                Rappture::Tooltip::cue $widget $mesg
620            }
621        }
622        default {
623            error "bad option \"$option\": should be coming, controls, now, start"
624        }
625    }
626}
627
628# ----------------------------------------------------------------------
629# USAGE: _plot ?<index> <options> <index> <options>...?
630#
631# Used internally to update the plot shown in the current result
632# viewer whenever the resultselector settings have changed.  Causes the
633# desired results to show up on screen.
634# ----------------------------------------------------------------------
635itcl::body Rappture::Analyzer::_plot {args} {
636    set _plotlist $args
637
638    set page [$itk_component(viewselector) value]
639    set page [$itk_component(viewselector) translate $page]
640    if {"" != $page} {
641        set f [$itk_component(resultpages) page $page]
642        $f.rviewer plot clear
643        foreach {index opts} $_plotlist {
644            $f.rviewer plot add $index $opts
645        }
646    }
647}
648
649# ----------------------------------------------------------------------
650# USAGE: _reorder <compList>
651#
652# Used internally to change the order of a series of output components
653# found in the <output> section.  Moves the <log> elements to the end
654# and returns the updated list.
655# ----------------------------------------------------------------------
656itcl::body Rappture::Analyzer::_reorder {comps} {
657    set i 0
658    set max [llength $comps]
659    while {$i < $max} {
660        set c [lindex $comps $i]
661        if {[string match log* $c]} {
662            set comps [lreplace $comps $i $i]
663            lappend comps $c
664            incr max -1
665        } else {
666            incr i
667        }
668    }
669    return $comps
670}
671
672# ----------------------------------------------------------------------
673# USAGE: _autoLabel <xmlobj> <path> <title> <cntVar>
674#
675# Used internally to check for an about.label property at the <path>
676# in <xmlobj>.  If this object doesn't have a label, then one is
677# supplied using the given <title>.  The <cntVar> is an array of
678# counters in the calling scopes for titles that have been used
679# in the past.  This is used to create titles like "Plot #2" the
680# second time it is encountered.
681#
682# The <xmlobj> is updated so that the label is inserted directly in
683# the tree.
684# ----------------------------------------------------------------------
685itcl::body Rappture::Analyzer::_autoLabel {xmlobj path title cntVar} {
686    upvar $cntVar counters
687
688    set group [$xmlobj get $path.about.group]
689    set label [$xmlobj get $path.about.label]
690    if {"" == $label} {
691        # no label -- make one up using the title specified
692        if {![info exists counters($group-$title)]} {
693            set counters($group-$title) 1
694            set label $title
695        } else {
696            set label "$title (#[incr counters($group-$title)])"
697        }
698        $xmlobj put $path.about.label $label
699    } else {
700        # handle the case of two identical labels in <output>
701        if {![info exists counters($group-$label)]} {
702            set counters($group-$label) 1
703        } else {
704            set label "$label (#[incr counters($group-$label)])"
705            $xmlobj put $path.about.label $label
706        }
707    }
708    return $label
709}
710
711# ----------------------------------------------------------------------
712# USAGE: _fixResult
713#
714# Used internally to change the result page being displayed whenever
715# the user selects a page from the results combobox.
716# ----------------------------------------------------------------------
717itcl::body Rappture::Analyzer::_fixResult {} {
718    set name [$itk_component(viewselector) value]
719    set page ""
720    if {"" != $name} {
721        set page [$itk_component(viewselector) translate $name]
722    }
723    if {$page == "@download"} {
724        # put the combobox back to its last value
725        $itk_component(viewselector) component entry configure -state normal
726        $itk_component(viewselector) component entry delete 0 end
727        $itk_component(viewselector) component entry insert end $_lastlabel
728        $itk_component(viewselector) component entry configure -state disabled
729        # perform the actual download
730        download start $itk_component(download)
731    } elseif {$page == "---"} {
732        # put the combobox back to its last value
733        $itk_component(viewselector) component entry configure -state normal
734        $itk_component(viewselector) component entry delete 0 end
735        $itk_component(viewselector) component entry insert end $_lastlabel
736        $itk_component(viewselector) component entry configure -state disabled
737    } elseif {$page != ""} {
738        set _lastlabel $name
739        set win [winfo toplevel $itk_component(hull)]
740        blt::busy hold $win
741        $itk_component(resultpages) current $page
742
743        set f [$itk_component(resultpages) page $page]
744        $f.rviewer plot clear
745        eval $f.rviewer plot add $_plotlist
746        blt::busy release [winfo toplevel $itk_component(hull)]
747    }
748}
749
750# ----------------------------------------------------------------------
751# USAGE: _fixResultSet ?<eventData>...?
752#
753# Used internally to react to changes within the ResultSet.  When a
754# result is added, a new result viewer is created for the object.
755# When all results are cleared, the viewers are deleted.
756# ----------------------------------------------------------------------
757itcl::body Rappture::Analyzer::_fixResultSet {args} {
758    array set eventData $args
759    switch -- $eventData(op) {
760        add {
761            set xmlobj $eventData(what)
762
763            # Detect molecule elements that contain trajectory data
764            # and convert to sequences.
765            _trajToSequence $xmlobj output
766
767            # Go through the analysis and find all result sets.
768            set haveresults 0
769            foreach item [_reorder [$xmlobj children output]] {
770                switch -glob -- $item {
771                    log* {
772                        _autoLabel $xmlobj output.$item "Output Log" counters
773                    }
774                    number* {
775                        _autoLabel $xmlobj output.$item "Number" counters
776                    }
777                    integer* {
778                        _autoLabel $xmlobj output.$item "Integer" counters
779                    }
780                    string* {
781                        _autoLabel $xmlobj output.$item "String" counters
782                    }
783                    histogram* - curve* - field* {
784                        _autoLabel $xmlobj output.$item "Plot" counters
785                    }
786                    drawing* {
787                        _autoLabel $xmlobj output.$item "Drawing" counters
788                    }
789                    structure* {
790                        _autoLabel $xmlobj output.$item "Structure" counters
791                    }
792                    table* {
793                        _autoLabel $xmlobj output.$item "Energy Levels" counters
794                    }
795                    sequence* {
796                        _autoLabel $xmlobj output.$item "Sequence" counters
797                    }
798                }
799                set label [$xmlobj get output.$item.about.group]
800                if {"" == $label} {
801                    set label [$xmlobj get output.$item.about.label]
802                }
803
804                set hidden [$xmlobj get output.$item.hide]
805                set hidden [expr {"" != $hidden && $hidden}]
806
807                if {"" != $label && !$hidden} {
808                    set haveresults 1
809                }
810            }
811
812            # if there are any valid results, add them to the resultset
813            if {$haveresults} {
814                set index [$_resultset get simnum $xmlobj]
815
816                # add each result to a result viewer
817                foreach item [_reorder [$xmlobj children output]] {
818                    set label [$xmlobj get output.$item.about.group]
819                    if {"" == $label} {
820                        set label [$xmlobj get output.$item.about.label]
821                    }
822                    set hidden [$xmlobj get output.$item.hide]
823                    if { $hidden == "" } {
824                        set hidden 0
825                    }
826                    if {"" != $label && !$hidden} {
827                        if {![info exists _label2page($label)]} {
828                            set name "page[incr _pages]"
829                            set page [$itk_component(resultpages) \
830                                insert end $name]
831                            set _label2page($label) $page
832                            set _label2desc($label) \
833                                [$xmlobj get output.$item.about.description]
834                            Rappture::ResultViewer $page.rviewer
835                            pack $page.rviewer -expand yes -fill both -pady 4
836
837                            set end [$itk_component(viewselector) \
838                                choices index -value ---]
839                            if {$end < 0} {
840                                set end "end"
841                            }
842                            $itk_component(viewselector) choices insert $end \
843                                $name $label
844                        }
845
846                        # add/replace the latest result into this viewer
847                        set page $_label2page($label)
848
849                        if {![info exists reset($page)]} {
850                            $page.rviewer clear $index
851                            set reset($page) 1
852                        }
853                        $page.rviewer add $index $xmlobj output.$item
854                    }
855                }
856            }
857
858            # show the first page by default
859            set max [$itk_component(viewselector) choices size]
860            for {set i 0} {$i < $max} {incr i} {
861                set first [$itk_component(viewselector) choices get -label $i]
862                if {$first != ""} {
863                    set page [$itk_component(viewselector) choices get -value $i]
864                    set char [string index $page 0]
865                    if {$char != "@" && $char != "-"} {
866                        $itk_component(resultpages) current $page
867                        $itk_component(viewselector) value $first
868                        set _lastlabel $first
869                        break
870                    }
871                }
872            }
873        }
874        clear {
875            if {$eventData(what) ne "all"} {
876                # delete this result from all viewers
877                array set params $eventData(what)
878                foreach label [array names _label2page] {
879                    set page $_label2page($label)
880                    $page.rviewer clear $params(simnum)
881                }
882            }
883
884            if {[$_resultset size] == 0} {
885                # reset the size of the controls area
886                set ht [winfo height $itk_component(results)]
887                set cntlht [$itk_component(resultselector) size -controlarea]
888                set frac [expr {double($cntlht)/$ht}]
889                $itk_component(results) fraction end $frac
890
891                foreach label [array names _label2page] {
892                    set page $_label2page($label)
893                    destroy $page.rviewer
894                }
895                $itk_component(resultpages) delete -all
896                set _pages 0
897
898                $itk_component(viewselector) value ""
899                $itk_component(viewselector) choices delete 0 end
900                catch {unset _label2page}
901                catch {unset _label2desc}
902                set _plotlist ""
903
904                $itk_component(viewselector) choices insert end --- "---"
905                $itk_component(viewselector) choices insert end \
906                    @download [Rappture::filexfer::label download]
907                set _lastlabel ""
908            }
909
910            # fix Simulate button state
911            reset
912        }
913        default {
914            error "don't know how to handle op \"$eventData(op)\""
915        }
916    }
917}
918
919# ----------------------------------------------------------------------
920# USAGE: _fixSize
921#
922# Used internally to change the size of the result set area whenever
923# a new control appears.  Adjusts the size available for the result
924# set up to some maximum.
925# ----------------------------------------------------------------------
926itcl::body Rappture::Analyzer::_fixSize {} {
927    set ht [winfo height $itk_component(results)]
928    if {$ht <= 1} { set ht [winfo reqheight $itk_component(results)] }
929    set cntlht [$itk_component(resultselector) size -controlarea]
930    set frac [expr {double($cntlht)/$ht}]
931
932    if {$frac < 0.4} {
933        $itk_component(results) fraction end $frac
934    }
935    _fixSimControl
936}
937
938# ----------------------------------------------------------------------
939# USAGE: _simState <boolean> ?<message>? ?<settings>?
940#
941# Used internally to change the "Simulation" button on or off.
942# If the <boolean> is on, then any <message> and <settings> are
943# displayed as well.  If the <boolean> is off, then only display
944# the message. The <message> is a note to the user about
945# what will be simulated, and the <settings> are a list of
946# tool parameter settings of the form {path1 val1 path2 val2 ...}.
947# When these are in place, the next Simulate operation will use
948# these settings.  This helps fill in missing data values.
949# ----------------------------------------------------------------------
950itcl::body Rappture::Analyzer::_simState {state args} {
951    if {$state} {
952        $itk_interior.simol configure \
953            -background $itk_option(-simcontrolactiveoutline)
954        configure -simcontrolcolor $itk_option(-simcontrolactivebackground)
955
956        $itk_component(abort) configure -state disabled
957        $itk_component(simulate) configure -state normal \
958            -command [itcl::code $this simulate]
959
960        #
961        # If there's a special message, then put it up next to the button.
962        #
963        set mesg [lindex $args 0]
964        if {"" != $mesg} {
965            $itk_component(simstatus) configure -state normal
966            $itk_component(simstatus) delete 1.0 end
967            $itk_component(simstatus) insert end $mesg
968
969            #
970            # If there are any settings, then install them in the
971            # "Simulate" button.  Also, pop them up as a tooltip
972            # for the message.
973            #
974            set settings [lindex $args 1]
975            if {[llength $settings] > 0} {
976                $itk_component(simulate) configure \
977                    -command [eval itcl::code $this simulate $settings]
978
979                set details ""
980                foreach {path val} $settings {
981                    set str [$_tool xml get $path.about.label]
982                    if {"" == $str} {
983                        set str [$_tool xml element -as id $path]
984                    }
985                    append details "$str = $val\n"
986                }
987                set details [string trim $details]
988
989                Rappture::Tooltip::for $itk_component(simstatus) $details
990                $itk_component(simstatus) insert end " "
991                $itk_component(simstatus) insert end "(details...)" popup
992            }
993            $itk_component(simstatus) configure -state disabled
994        }
995    } else {
996        if {"" != $itk_option(-simcontrolbackground)} {
997            set simcbg $itk_option(-simcontrolbackground)
998        } else {
999            set simcbg $itk_option(-background)
1000        }
1001        $itk_interior.simol configure \
1002            -background $itk_option(-simcontroloutline)
1003        configure -simcontrolcolor $simcbg
1004
1005        $itk_component(simulate) configure -state disabled
1006        $itk_component(abort) configure -state normal
1007
1008        $itk_component(simstatus) configure -state normal
1009        $itk_component(simstatus) delete 1.0 end
1010        set mesg [lindex $args 0]
1011        if {"" != $mesg} {
1012            $itk_component(simstatus) insert end $mesg
1013        }
1014        $itk_component(simstatus) configure -state disabled
1015    }
1016}
1017
1018# ----------------------------------------------------------------------
1019# USAGE: _simOutput <message>
1020#
1021# Invoked automatically whenever output comes in while running the
1022# tool.  Extracts any =RAPPTURE-???=> messages from the output and
1023# sends the output to the display.  For example, any
1024# =RAPPTURE-PROGRESS=> message pops up the progress meter and updates
1025# it to show the latest progress message.  This is useful for
1026# long-running tools, to let the user know how much longer the
1027# simulation will take.
1028# ----------------------------------------------------------------------
1029itcl::body Rappture::Analyzer::_simOutput {message} {
1030    #
1031    # Scan through and pick out any =RAPPTURE-PROGRESS=> messages first.
1032    #
1033    while {[regexp -indices \
1034               {=RAPPTURE-PROGRESS=> *([-+]?[0-9]+) +([^\n]*)(\n|$)} $message \
1035                match percent mesg]} {
1036
1037        foreach {i0 i1} $percent break
1038        set percent [string range $message $i0 $i1]
1039
1040        foreach {i0 i1} $mesg break
1041        set mesg [string range $message $i0 $i1]
1042
1043        pack $itk_component(progress) -fill x -padx 10 -pady 10
1044        $itk_component(progress) settings -percent $percent -message $mesg
1045
1046        foreach {i0 i1} $match break
1047        set message [string replace $message $i0 $i1]
1048    }
1049
1050    #
1051    # Break up the remaining lines according to =RAPPTURE-ERROR=> messages.
1052    # Show errors in a special color.
1053    #
1054    $itk_component(runinfo) configure -state normal
1055
1056    while {[regexp -indices \
1057               {=RAPPTURE-([a-zA-Z]+)=>([^\n]*)(\n|$)} $message \
1058                match type mesg]} {
1059
1060        foreach {i0 i1} $match break
1061        set first [string range $message 0 [expr {$i0-1}]]
1062        if {[string length $first] > 0} {
1063            $itk_component(runinfo) insert end $first
1064            $itk_component(runinfo) insert end \n
1065        }
1066
1067        foreach {t0 t1} $type break
1068        set type [string range $message $t0 $t1]
1069        foreach {m0 m1} $mesg break
1070        set mesg [string range $message $m0 $m1]
1071        if {[string length $mesg] > 0 && $type != "RUN"} {
1072            $itk_component(runinfo) insert end $mesg $type
1073            $itk_component(runinfo) insert end \n $type
1074        }
1075
1076        set message [string range $message [expr {$i1+1}] end]
1077    }
1078
1079    if {[string length $message] > 0} {
1080        $itk_component(runinfo) insert end $message
1081        if {[$itk_component(runinfo) get end-2char] != "\n"} {
1082            $itk_component(runinfo) insert end "\n"
1083        }
1084        $itk_component(runinfo) see end
1085    }
1086    $itk_component(runinfo) configure -state disabled
1087}
1088
1089# ----------------------------------------------------------------------
1090# USAGE: _resultTooltip
1091#
1092# Used internally to build the tooltip string displayed for the
1093# result selector.  If the current page has an associated description,
1094# then it is displayed beneath the result.
1095#
1096# Returns the string for the tooltip.
1097# ----------------------------------------------------------------------
1098itcl::body Rappture::Analyzer::_resultTooltip {} {
1099    set tip ""
1100    set name [$itk_component(viewselector) value]
1101    if {[info exists _label2desc($name)] &&
1102         [string length $_label2desc($name)] > 0} {
1103        append tip "$_label2desc($name)\n\n"
1104    }
1105    if {[array size _label2page] > 1} {
1106        append tip "Use this control to display other output results."
1107    }
1108    return $tip
1109}
1110
1111# ----------------------------------------------------------------------
1112# USAGE: _fixSimControl
1113#
1114# Used internally to add or remove the simulation control at the
1115# top of the analysis area.  This is controlled by the -simcontrol
1116# option.
1117# ----------------------------------------------------------------------
1118itcl::body Rappture::Analyzer::_fixSimControl {} {
1119    switch -- $itk_option(-simcontrol) {
1120        on {
1121            pack $itk_interior.simol -fill x -before $itk_interior.nb
1122        }
1123        off {
1124            pack forget $itk_interior.simol
1125        }
1126        auto {
1127            #
1128            # If we have two or more radiodials, then there is a
1129            # chance of encountering a combination of parameters
1130            # with no data, requiring simulation.
1131            #
1132            if {[$itk_component(resultselector) size -controls] >= 2} {
1133                pack $itk_interior.simol -fill x -before $itk_interior.nb
1134            } else {
1135                pack forget $itk_interior.simol
1136            }
1137        }
1138        default {
1139            error "bad value \"$itk_option(-simcontrol)\": should be on, off, auto"
1140        }
1141    }
1142}
1143
1144# ----------------------------------------------------------------------
1145# USAGE: _fixNotebook
1146#
1147# Used internally to switch the active notebook page
1148# ----------------------------------------------------------------------
1149itcl::body Rappture::Analyzer::_fixNotebook {} {
1150    switch -- $itk_option(-notebookpage) {
1151        about {
1152            $itk_component(notebook) current about
1153        }
1154        simulate {
1155            $itk_component(notebook) current simulate
1156        }
1157        analyze {
1158            $itk_component(notebook) current analyze
1159        }
1160        default {
1161            error "bad value \"$itk_option(-notebookpage)\": should be about, simulate, analyze"
1162        }
1163    }
1164}
1165
1166# ----------------------------------------------------------------------
1167# USAGE: _isPdbTrajectory <data>
1168#
1169# Used internally to determine whether pdb or lammps data represents a
1170# trajectory rather than a single frame
1171# ----------------------------------------------------------------------
1172itcl::body Rappture::Analyzer::_isPdbTrajectory {data} {
1173    if { [llength $data]  == 0 } {
1174        return 0
1175    }
1176    set nModels 0
1177    foreach line $data {
1178        if { [string match "MODEL*" $line] }  {
1179            incr nModels
1180            if { $nModels > 1 } {
1181                # Stop if more than one model found.  No need to count them
1182                # all.
1183                return 1
1184            }
1185        }
1186    }
1187    return 0
1188}
1189
1190# ----------------------------------------------------------------------
1191# USAGE: _isLammpsTrajectory <data>
1192#
1193# Used internally to determine whether pdb or lammps data represents a
1194# trajectory rather than a single frame
1195# ----------------------------------------------------------------------
1196itcl::body Rappture::Analyzer::_isLammpsTrajectory { data } {
1197    if { [llength $data]  == 0 } {
1198        return 0
1199    }
1200    set nModels 0
1201    foreach line $data {
1202        if { [regexp {^[\t ]*ITEM:[ \t]+TIMESTEP} $line] } {
1203            incr nModels
1204            if { $nModels > 1 } {
1205                # Stop if more than one model found.  No need to count them
1206                # all.
1207                return 1
1208            }
1209        }
1210    }
1211    return 0
1212}
1213
1214# ----------------------------------------------------------------------
1215# USAGE: _pdbToSequence <xmlobj> ?<path>?
1216#
1217# If the molecule element is a trajectory, delete the original
1218# and create a sequence of individual molecules.
1219# Used internally to detect any molecule output elements that contain
1220# trajectory data.  Trajectories will be converted into sequences of
1221# individual molecules.  All other elements will be unaffected. Scans
1222# the entire xml tree if a starting path is not specified.
1223# ----------------------------------------------------------------------
1224itcl::body Rappture::Analyzer::_pdbToSequence {xmlobj path id child data} {
1225
1226    set seqLabel [$xmlobj get ${child}.about.label]
1227    set descr    [$xmlobj get ${child}.about.description]
1228    set formula  [$xmlobj get ${child}.components.molecule.formula]
1229    $xmlobj remove $child
1230
1231    set sequence  $path.sequence($id)
1232    $xmlobj put ${sequence}.about.label $seqLabel
1233    $xmlobj put ${sequence}.about.description $descr
1234    $xmlobj put ${sequence}.index.label "Frame"
1235
1236    set frameNum 0
1237    set numLines [llength $data]
1238    for { set i 0 } { $i < $numLines } { incr i } {
1239        set line [lindex $data $i]
1240        set line [string trim $line]
1241        set contents {}
1242        if { [string match "MODEL*" $line] } {
1243            # Save the contents until we get an ENDMDL record.
1244            for {} { $i < $numLines } { incr i } {
1245                set line [lindex $data $i]
1246                set line [string trim $line]
1247                if { $line == "" } {
1248                    continue;           # Skip blank lines.
1249                }
1250                if { [string match "ENDMDL*" $line] } {
1251                    break;
1252                }
1253                append contents $line\n
1254            }
1255            set frame ${sequence}.element($frameNum)
1256            $xmlobj put ${frame}.index $frameNum
1257           
1258            set molecule ${frame}.structure.components.molecule
1259            $xmlobj put ${molecule}.pdb $contents
1260            $xmlobj put ${molecule}.formula $formula
1261            incr frameNum
1262        }
1263    }
1264}
1265
1266# ----------------------------------------------------------------------
1267# USAGE: _lammpsToSequence <xmlobj> ?<path>?
1268#
1269# If the molecule element is a trajectory, delete the original
1270# and create a sequence of individual molecules.
1271# Used internally to detect any molecule output elements that contain
1272# trajectory data.  Trajectories will be converted into sequences of
1273# individual molecules.  All other elements will be unaffected. Scans
1274# the entire xml tree if a starting path is not specified.
1275# ----------------------------------------------------------------------
1276itcl::body Rappture::Analyzer::_lammpsToSequence {xmlobj path id child data} {
1277
1278    set seqLabel [$xmlobj get ${child}.about.label]
1279    set descr    [$xmlobj get ${child}.about.description]
1280    set typemap  [$xmlobj get ${child}.components.molecule.lammpstypemap]
1281    $xmlobj remove $child
1282
1283    set sequence ${path}.sequence($id)
1284    $xmlobj put ${sequence}.about.label $seqLabel
1285    $xmlobj put ${sequence}.about.description $descr
1286    $xmlobj put ${sequence}.index.label "Frame"
1287
1288    set frameNum 0
1289    set frameContents ""
1290    set inModel 0
1291    foreach line $data {
1292        set line [string trim $line]
1293        if { $line == "" } {
1294            continue;                   # Skip blank lines
1295        }
1296        if {[regexp {^[\t ]*ITEM:[ \t]+ATOMS} $line] } {
1297            if { $inModel && $frameContents != "" } {
1298                set frame ${sequence}.element($frameNum)
1299                $xmlobj put ${frame}.index $frameNum
1300               
1301                set molecule ${frame}.structure.components.molecule
1302                $xmlobj put ${molecule}.lammps $frameContents
1303                $xmlobj put ${molecule}.lammpstypemap $typemap
1304               
1305                incr frameNum
1306                set frameContents ""
1307            }
1308            set inModel 1
1309        } elseif { [scan $line "%d %d %f %f %f" a b c d e] == 5 } {
1310            if { !$inModel } {
1311                puts stderr "found \"$line\" without previous \"ITEM: ATOMS\""
1312                set inModel 1
1313            }
1314            append frameContents $line\n
1315        }
1316    }
1317    if { $frameContents != "" } {
1318        set frame ${sequence}.element($frameNum)
1319        $xmlobj put ${frame}.index $frameNum
1320       
1321        set molecule ${frame}.structure.components.molecule
1322        $xmlobj put ${molecule}.lammps $frameContents
1323        $xmlobj put ${molecule}.lammpstypemap $typemap
1324    }
1325}
1326
1327# ----------------------------------------------------------------------
1328# USAGE: _trajToSequence <xmlobj> ?<path>?
1329#
1330#       Check for PDB and LAMMPS trajectories in molecule data and rewrite
1331#       the individual models as a sequence of molecules.  Used internally
1332#       to detect any molecule output elements that contain trajectory data.
1333#       Trajectories will be converted into sequences of individual molecules.
1334#       All other elements will be unaffected. Scans the entire xml tree if a
1335#       starting path is not specified. 
1336#
1337# ----------------------------------------------------------------------
1338itcl::body Rappture::Analyzer::_trajToSequence {xmlobj {path ""}} {
1339    # Remove leading dot from path, if present.
1340    if { [string index $path 0] == "." } {
1341        set path [string range $path 1 end]
1342    }
1343    # Otherwise check each child.
1344    foreach child [$xmlobj children $path] {
1345        set current ${path}.${child}
1346        if { [string match "structure*" $child] } {
1347            set isTraj [$xmlobj get ${current}.components.molecule.trajectory]
1348            if { $isTraj == "" || !$isTraj } {
1349                continue;               # Not a trajectory.
1350            }
1351            # Look for trajectory if molecule element found.  Check both pdb
1352            # data and lammps data.
1353            set type [$xmlobj element -as type $current]
1354            set id   [$xmlobj element -as id $current]
1355            set pdbdata    [$xmlobj get ${current}.components.molecule.pdb]
1356            set lammpsdata [$xmlobj get ${current}.components.molecule.lammps]
1357            if { $pdbdata != "" && $lammpsdata != "" } {
1358                puts stderr \
1359                    "found both <pdb> and <lammps> elements: picking pdb"
1360            }
1361            set pdbdata [split $pdbdata \n]
1362            set lammpsdata [split $lammpsdata \n]
1363            if { [_isPdbTrajectory $pdbdata] } {
1364                _pdbToSequence $xmlobj $path $id $current $pdbdata
1365            } elseif { [_isLammpsTrajectory $lammpsdata] } {
1366                _lammpsToSequence $xmlobj $path $id $current $lammpsdata
1367            }
1368            continue
1369        }
1370        if 0 {
1371        # Recurse over all child nodes.
1372        _trajToSequence $xmlobj $current
1373        }
1374    }
1375}
1376
1377# ----------------------------------------------------------------------
1378# CONFIGURATION OPTION: -simcontrol
1379#
1380# Controls whether or not the Simulate button is showing.  In some
1381# cases, it is not needed.
1382# ----------------------------------------------------------------------
1383itcl::configbody Rappture::Analyzer::simcontrol {
1384    _fixSimControl
1385}
1386
1387# ----------------------------------------------------------------------
1388# CONFIGURATION OPTION: -notebookpage
1389#
1390# Controls which page of the analyzer notebook is shown. It is
1391# particularly needed when using rerun, when you don't want to
1392# "simulate -ifneeded" because an actual simulation might be
1393# kicked off due to differences between tool.xml and run.xml
1394# ----------------------------------------------------------------------
1395itcl::configbody Rappture::Analyzer::notebookpage {
1396    _fixNotebook
1397}
Note: See TracBrowser for help on using the repository browser.