source: branches/1.7/gui/scripts/analyzer.tcl @ 6705

Last change on this file since 6705 was 6705, checked in by clarksm, 6 years ago

Enable "ionhelper" to execute jobs and immediately put results in cache.
This includes reporting run.xml location with absolute path.

File size: 78.2 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2# ----------------------------------------------------------------------
3#  COMPONENT: analyzer - output area for Rappture
4#
5#  This widget acts as the output side of a Rappture application.
6#  When the input has changed, it displays a Simulate button that
7#  launches the simulation.  When a simulation is running, this
8#  area shows status.  When it is finished, the results appear
9#  in place of the button, according to the specs in the <analyze>
10#  XML data.
11# ======================================================================
12#  AUTHOR:  Michael McLennan, Purdue University
13#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
14#
15#  See the file "license.terms" for information on usage and
16#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
17# ======================================================================
18package require Itk
19
20option add *Analyzer.width 3.5i widgetDefault
21option add *Analyzer.height 4i widgetDefault
22option add *Analyzer.simControl "auto" widgetDefault
23option add *Analyzer.simControlBackground "" widgetDefault
24option add *Analyzer.simControlOutline gray widgetDefault
25option add *Analyzer.simControlActiveBackground #ffffcc widgetDefault
26option add *Analyzer.simControlActiveOutline black widgetDefault
27option add *Analyzer.notebookpage "about" widgetDefault
28
29option add *Analyzer.font \
30    -*-helvetica-medium-r-normal-*-12-* widgetDefault
31option add *Analyzer.codeFont \
32    -*-courier-medium-r-normal-*-12-* widgetDefault
33option add *Analyzer.textFont \
34    -*-helvetica-medium-r-normal-*-12-* widgetDefault
35option add *Analyzer.boldTextFont \
36    -*-helvetica-bold-r-normal-*-12-* widgetDefault
37
38itcl::class Rappture::Analyzer {
39    inherit itk::Widget
40
41    itk_option define -codefont codeFont Font ""
42    itk_option define -textfont textFont Font ""
43    itk_option define -boldtextfont boldTextFont Font ""
44    itk_option define -simcontrol simControl SimControl ""
45    itk_option define -simcontroloutline simControlOutline Background ""
46    itk_option define -simcontrolbackground simControlBackground Background ""
47    itk_option define -simcontrolactiveoutline simControlActiveOutline Background ""
48    itk_option define -simcontrolactivebackground simControlActiveBackground Background ""
49    itk_option define -holdwindow holdWindow HoldWindow ""
50    itk_option define -notebookpage notebookPage NotebookPage ""
51
52    constructor {tool args} {
53        # defined below
54    }
55    destructor {
56        # defined below
57    }
58
59    public method simulate {args}
60    public method reset {{when -eventually}}
61    public method load {xmlobj}
62    public method reload { fileName }
63    public method clear {{xmlobj "all"}}
64    public method download {option args}
65    public method buildMenu { w url appName }
66
67    protected method _plot {args}
68    protected method _reorder {comps}
69    protected method _autoLabel {xmlobj path title cntVar}
70    protected method _fixResult {}
71    protected method _fixResultSet {args}
72    protected method _fixSize {}
73    protected method _fixSimControl {}
74    protected method _fixNotebook {}
75    protected method _simState {state args}
76    protected method _simLogError {message}
77    protected method _simLogOutput {message}
78    protected method _simOutput {message}
79    protected method _resultTooltip {}
80    protected method _isPdbTrajectory {data}
81    protected method _isLammpsTrajectory {data}
82    protected method _pdbToSequence {xmlobj path id child data}
83    protected method _lammpsToSequence {xmlobj path id child data}
84    protected method _trajToSequence {xmlobj {path ""}}
85    protected method _pop_uq_dialog {win}
86    protected method _setWaitVariable {state}
87    protected method _adjust_level {win}
88
89    private variable _done 0
90    private variable _tree ""
91    private variable _saved
92    private variable _name ""
93    private variable _revision 0;       # Tool revision
94    private variable _tool ""          ;# belongs to this tool
95    private variable _appName ""       ;# Name of application
96    private variable _control "manual" ;# start mode
97    private variable _resultset ""     ;# ResultSet object with all results
98    private variable _pages 0          ;# number of pages for result sets
99    private variable _label2page       ;# maps output label => result set
100    private variable _label2desc       ;# maps output label => description
101    private variable _label2item       ;# maps output label => output.xxx item
102    private variable _lastlabel ""     ;# label of last example loaded
103    private variable _plotlist ""      ;# items currently being plotted
104    private variable _lastPlot
105    private variable _uq_active 0      ;# a UQ variables has been used
106    private variable _wait_uq 0
107    private common job                 ;# array var used for blt::bgexec jobs
108
109    private method BuildQuestionDialog { popup }
110    private method BuildSimulationTable { w }
111    private method Cancel {}
112    private method CheckSimsetDetails {}
113    private method CleanName { name }
114    private method CreateSharedPath { path }
115    private method EditSimset {}
116    private method ExportFile { src msgFile }
117    private method ExportURL { appName installdir }
118    private method FindSimsetsForApp { appName }
119    private method GetSimset { name }
120    private method InstallSharedFile { installdir file }
121    private method LoadSimulations { runfiles }
122    private method Ok {}
123    private method OverwriteSaveFile {}
124    private method PostMenu { menu }
125    private method ReadSimsetFile { fileName }
126    private method Save {}
127    private method SaveSimulations {}
128    private method SelectSimsetForDeletion {}
129    private method SelectSimsetForLoading {}
130    private method SelectSimsetNameAndSave {}
131    private method SelectSimsetToShare {}
132    private method WriteSimsetFile { appName fileName share }
133}
134
135itk::usual Analyzer {
136    keep -background -cursor foreground -font
137}
138
139# ----------------------------------------------------------------------
140# CONSTRUCTOR
141# ----------------------------------------------------------------------
142itcl::body Rappture::Analyzer::constructor {tool args} {
143    set _tool $tool
144    set _tree [blt::tree create]
145
146    # use this to store all simulation results
147    set _resultset [Rappture::ResultSet ::\#auto]
148    $_resultset notify add $this [itcl::code $this _fixResultSet]
149
150    # widget settings...
151    itk_option add hull.width hull.height
152    pack propagate $itk_component(hull) no
153
154    frame $itk_interior.simol -borderwidth 1 -relief flat
155    pack $itk_interior.simol -fill x
156
157    itk_component add simbg {
158        frame $itk_interior.simol.simbg -borderwidth 0
159    } {
160        usual
161        rename -background -simcontrolcolor simControlColor Color
162    }
163    pack $itk_component(simbg) -expand yes -fill both
164
165    set simtxt [string trim [$tool xml get tool.action.label]]
166    if {"" == $simtxt} {
167        set simtxt "Simulate"
168    }
169    itk_component add simulate {
170        button $itk_component(simbg).simulate -text $simtxt \
171            -command [itcl::code $this simulate]
172    } {
173        usual
174        rename -highlightbackground -simcontrolcolor simControlColor Color
175    }
176    pack $itk_component(simulate) -side left -padx 4 -pady 4
177
178    # BE CAREFUL: Shift focus when we click on "Simulate".
179    #   This shifts focus away from input widgets and causes them to
180    #   finalize and log any pending changes.  A better way would be
181    #   to have the "sync" operation finalize/sync, but this works
182    #   for now.
183    bind $itk_component(simulate) <ButtonPress> {focus %W}
184
185    # if there's a hub url, then add "About" and "Questions" links
186    global rapptureInfo
187    if { $rapptureInfo(appName) == "" } {
188        set _appName [$_tool xml get "tool.id"]
189    } else {
190        set _appName $rapptureInfo(appName)
191    }
192    set num [$_tool xml get "tool.version.application.revision"]
193    if { $num == "" } {
194        set num 0
195    }
196    set _revision $num
197    set url [Rappture::Tool::resources -huburl]
198
199    itk_component add hubcntls {
200        frame $itk_component(simbg).hubcntls
201    } {
202        usual
203        rename -background -simcontrolcolor simControlColor Color
204    }
205    pack $itk_component(hubcntls) -side right -padx 4
206   
207    itk_component add help {
208        menubutton $itk_component(hubcntls).help \
209            -image [Rappture::icon hamburger_menu] \
210            -highlightthickness 0 \
211            -menu $itk_component(hubcntls).help.menu
212    } {
213        usual
214        ignore -highlightthickness
215        rename -background -simcontrolcolor simControlColor Color
216    }
217    pack $itk_component(help) -side left
218    buildMenu $itk_component(help).menu $url $_appName
219   
220    itk_component add simstatus {
221        text $itk_component(simbg).simstatus -borderwidth 0 \
222            -highlightthickness 0 -height 1 -width 1 -wrap none \
223            -state disabled
224    } {
225        usual
226        ignore -highlightthickness
227        rename -background -simcontrolcolor simControlColor Color
228        rename -font -textfont textFont Font
229    }
230    pack $itk_component(simstatus) -side left -expand yes -fill x
231
232    $itk_component(simstatus) tag configure popup \
233        -underline 1 -foreground blue
234
235    $itk_component(simstatus) tag bind popup \
236        <Enter> {%W configure -cursor center_ptr}
237    $itk_component(simstatus) tag bind popup \
238        <Leave> {%W configure -cursor ""}
239    $itk_component(simstatus) tag bind popup \
240        <ButtonPress> {after idle {Rappture::Tooltip::tooltip show %W}}
241
242
243    itk_component add notebook {
244        Rappture::Notebook $itk_interior.nb
245    }
246    pack $itk_interior.nb -expand yes -fill both
247
248    # ------------------------------------------------------------------
249    # ABOUT PAGE
250    # ------------------------------------------------------------------
251    set w [$itk_component(notebook) insert end about]
252
253    Rappture::Scroller $w.info -xscrollmode off -yscrollmode auto
254    pack $w.info -expand yes -fill both -padx 4 -pady 20
255    itk_component add toolinfo {
256        text $w.info.text -width 1 -height 1 -wrap word \
257            -borderwidth 0 -highlightthickness 0
258    } {
259        usual
260        ignore -borderwidth -relief
261        rename -font -textfont textFont Font
262    }
263    $w.info contents $w.info.text
264
265    # ------------------------------------------------------------------
266    # SIMULATION PAGE
267    # ------------------------------------------------------------------
268    set w [$itk_component(notebook) insert end simulate]
269    frame $w.cntls
270    pack $w.cntls -side bottom -fill x -pady 12
271    frame $w.cntls.sep -background black -height 1
272    pack $w.cntls.sep -side top -fill x
273
274    itk_component add abort {
275        button $w.cntls.abort -text "Abort" \
276            -command [itcl::code $_tool abort]
277    }
278    pack $itk_component(abort) -side left -expand yes -padx 4 -pady 4
279
280    Rappture::Scroller $w.info -xscrollmode auto -yscrollmode auto
281    pack $w.info -expand yes -fill both -padx 4 -pady 4
282    itk_component add runinfo {
283        text $w.info.text -width 1 -height 1 -wrap none \
284            -borderwidth 0 -highlightthickness 0 \
285            -state disabled
286    } {
287        usual
288        ignore -borderwidth -relief
289        rename -font -codefont codeFont Font
290    }
291    $w.info contents $w.info.text
292
293    itk_component add progress {
294        Rappture::Progress $w.progress
295    }
296
297    # ------------------------------------------------------------------
298    # ANALYZE PAGE
299    # ------------------------------------------------------------------
300    set w [$itk_component(notebook) insert end analyze]
301
302    frame $w.top
303    pack $w.top -side top -fill x -pady 8
304    label $w.top.l -text "Result:" -font $itk_option(-font)
305    pack $w.top.l -side left
306
307    itk_component add viewselector {
308        Rappture::Combobox $w.top.sel -width 10 -editable no
309    } {
310        usual
311        rename -font -textfont textFont Font
312    }
313    pack $itk_component(viewselector) -side left -expand yes -fill x
314    bind $itk_component(viewselector) <<Value>> [itcl::code $this _fixResult]
315    bind $itk_component(viewselector) <Enter> \
316        [itcl::code $this download coming]
317
318    Rappture::Tooltip::for $itk_component(viewselector) \
319        "@[itcl::code $this _resultTooltip]"
320
321    $itk_component(viewselector) choices insert end \
322        --- "---"
323
324    itk_component add download {
325        button $w.top.dl -image [Rappture::icon download] -anchor e \
326            -borderwidth 1 -relief flat -overrelief raised \
327            -command [itcl::code $this download start $w.top.dl]
328    }
329    pack $itk_component(download) -side right -padx {4 0}
330    bind $itk_component(download) <Enter> \
331        [itcl::code $this download coming]
332
333    $itk_component(viewselector) choices insert end \
334        @download [Rappture::filexfer::label download]
335
336    if {[Rappture::filexfer::enabled]} {
337        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.
338
339NOTE:  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."
340    } else {
341        Rappture::Tooltip::for $itk_component(download) "Saves the current result to a file on your desktop."
342    }
343
344    itk_component add results {
345        Rappture::Panes $w.pane \
346            -sashwidth 2 -sashrelief solid -sashpadding {2 0}
347    } {
348        usual
349        ignore -sashwidth -sashrelief -sashpadding
350    }
351    pack $itk_component(results) -expand yes -fill both
352    set f [$itk_component(results) pane 0]
353
354    itk_component add resultpages {
355        Rappture::Notebook $f.nb
356    }
357    pack $itk_component(resultpages) -expand yes -fill both
358    set f [$itk_component(results) insert end -fraction 0.1]
359    itk_component add resultselector {
360        Rappture::ResultSelector $f.rsel -resultset $_resultset \
361            -settingscommand [itcl::code $this _plot]
362    }
363    pack $itk_component(resultselector) -expand yes -fill both
364    bind $itk_component(resultselector) <<Layout>> [itcl::code $this _fixSize]
365    bind $itk_component(results) <Configure> [itcl::code $this _fixSize]
366
367    eval itk_initialize $args
368
369    $itk_component(runinfo) tag configure ERROR -foreground red
370    $itk_component(runinfo) tag configure text -font $itk_option(-textfont)
371
372    #
373    # Load up tool info on the first page.
374    #
375    $itk_component(toolinfo) tag configure title \
376        -font $itk_option(-boldtextfont)
377
378    set mesg [string trim [$tool xml get tool.title]]
379    if {"" != $mesg} {
380        $itk_component(toolinfo) insert end $mesg title
381        $itk_component(toolinfo) insert end "\n\n"
382    }
383
384    set mesg [string trim [$tool xml get tool.about]]
385    if {"" != $mesg} {
386        $itk_component(toolinfo) insert end $mesg
387    }
388    $itk_component(toolinfo) configure -state disabled
389    $itk_component(notebook) current about
390
391    # tool can run on "manual" (default) or "auto"
392    set cntl [string trim [$tool xml get tool.control]]
393    if {"" == $cntl} {
394        set cntl [string trim [$tool xml get tool.control.type]]
395    }
396    if {"" != $cntl} {
397        set _control $cntl
398    }
399
400    # reset everything to a clean state
401    reset
402}
403
404# ----------------------------------------------------------------------
405# DESTRUCTOR
406# ----------------------------------------------------------------------
407itcl::body Rappture::Analyzer::destructor {} {
408    after cancel [itcl::code $this simulate]
409    $_resultset notify remove $this
410    itcl::delete object $_resultset
411}
412
413# ----------------------------------------------------------------------
414# USAGE: simulate ?-ifneeded?
415# USAGE: simulate ?<path1> <value1> <path2> <value2> ...?
416#
417# Kicks off the simulator by executing the tool.command associated
418# with the tool.  If any arguments are specified, they are used to
419# set parameters for the simulation.  While the simulation is running,
420# it shows status.  When the simulation is finished, it switches
421# automatically to "analyze" mode and shows the results.
422# ----------------------------------------------------------------------
423itcl::body Rappture::Analyzer::simulate {args} {
424
425    set uq [$_tool get_uq -uq_type smolyak -uq_args 2]
426
427    # pop up UQ window
428    if {[$uq num_runs] > 1} {
429        set _uq_active 1
430        set status [$uq run_dialog $itk_component(simulate)]
431        if {$status == 0} {
432            # cancelled
433            return
434        }
435        lappend args -uq_type [$uq type]
436        lappend args -uq_args [$uq args]
437        # Need to put these UQ values into the driver file
438        # so the call to resultset::contains will be correct.
439        set _xml [$_tool xml object]
440        $_xml put uq.type.current [$uq type]
441        $_xml put uq.args.current [$uq args]
442        $_xml put uq.args.about.label "level"
443        $_xml put uq.args.about.description "Polynomial Degree of Smolyak GPC method."
444    }
445    #puts "simulate args=$args"
446
447    if {[lindex $args 0] == "-ifneeded"} {
448        # check to see if simulation is really needed
449        $_tool sync
450        if {[$_resultset contains [$_tool xml object]] &&
451            ![string equal $_control "manual-resim"]} {
452            # not needed -- show results and return
453            $itk_component(notebook) current analyze
454            return
455        }
456        set args [lreplace $args 0 0]
457    }
458
459    # simulation is needed -- go to simulation page
460    $itk_component(notebook) current simulate
461
462    # no progress messages yet
463    pack forget $itk_component(progress)
464    lappend args -output [itcl::code $this _simOutput]
465    lappend args -stderr [itcl::code $this _simLogError]
466#   lappend args -stdout [itcl::code $this _simLogOutput]
467
468    _simState off
469    $itk_component(runinfo) configure -state normal
470    $itk_component(runinfo) delete 1.0 end
471    $itk_component(runinfo) insert end "Running simulation...\n\n" text
472    $itk_component(runinfo) configure -state disabled
473
474    # if the hold window is set, then put up a busy cursor
475    if {$itk_option(-holdwindow) != ""} {
476        blt::busy hold $itk_option(-holdwindow)
477        raise $itk_component(hull)
478    }
479
480    # execute the job
481    #puts "$_tool run $args"
482
483    foreach {status result} [eval $_tool run $args] break
484
485    # if job was aborted, then allow simulation again
486    if {$result == "ABORT"} {
487        _simState on "Aborted"
488    }
489
490    # load results from run.xml into analyzer
491    if {$status == 0 && $result != "ABORT"} {
492        set status [catch {load $result} result]
493    }
494
495    # back to normal
496    if {$itk_option(-holdwindow) != ""} {
497        blt::busy release $itk_option(-holdwindow)
498    }
499    $itk_component(abort) configure -state disabled
500
501    if {$status != 0} {
502        $itk_component(runinfo) configure -state normal
503        # Don't erase program error messages.
504        # $itk_component(runinfo) delete 1.0 end
505        $itk_component(runinfo) insert end "\n\nProblem launching job:\n\n" text
506        _simOutput $result
507        $itk_component(runinfo) configure -state disabled
508        $itk_component(runinfo) see 1.0
509
510        # Try to create a support ticket for this error.
511        # It may be a real problem.
512        if {[Rappture::bugreport::shouldReport for jobs]} {
513            set ::errorInfo "\n\n== RAPPTURE INPUT ==\n[$_tool xml xml]"
514            Rappture::bugreport::register "Problem launching job:\n$result"
515            Rappture::bugreport::attachment [$_tool xml xml]
516            Rappture::bugreport::send
517        }
518    } else {
519        $itk_component(notebook) current analyze
520    }
521
522    # do this last -- after _simOutput above
523    pack forget $itk_component(progress)
524}
525
526
527# ----------------------------------------------------------------------
528# USAGE: reset ?-eventually|-now?
529#
530# Used to reset the analyzer whenever the input to a simulation has
531# changed.  Sets the mode back to "simulate", so the user has to
532# simulate again to see the output.  If the <start> option is set
533# to "auto", the simulation is invoked immediately.
534# ----------------------------------------------------------------------
535itcl::body Rappture::Analyzer::reset {{when -eventually}} {
536    if {$when == "-eventually"} {
537        after cancel [list catch [itcl::code $this reset -now]]
538        after idle [list catch [itcl::code $this reset -now]]
539        return
540    }
541
542    # check to see if simulation is really needed
543    $_tool sync
544    if {![$_resultset contains [$_tool xml object]] ||
545        [string equal $_control "manual-resim"]} {
546        # if control mode is "auto", then simulate right away
547        if {[string match auto* $_control]} {
548            # auto control -- don't need button
549            pack forget $itk_interior.simol
550
551            after cancel [itcl::code $this simulate]
552            after idle [itcl::code $this simulate]
553        } else {
554            _simState on "new input parameters"
555        }
556    } else {
557        _simState off
558    }
559}
560
561# ----------------------------------------------------------------------
562# USAGE: load <xmlobj>
563#
564# Loads the data from the given <xmlobj> into the appropriate results
565# sets.  If necessary, new results sets are created to store the data.
566# ----------------------------------------------------------------------
567itcl::body Rappture::Analyzer::load {xmlobj} {
568    # only show the last result? then clear first
569    if {[string trim [$_tool xml get tool.analyzer]] == "last"} {
570        clear
571    }
572    #puts "Analyzer::load"
573    $_resultset add $xmlobj
574
575    # NOTE: Adding will trigger a !change event on the ResultSet
576    # object, which will trigger calls to _fixResultSet to add
577    # the results to display.
578}
579
580# ----------------------------------------------------------------------
581# USAGE: clear ?<xmlobj>?
582#
583# Discards one or more results previously loaded into the analyzer.
584# If an <xmlobj> is specified, then that one result is cleared.
585# Otherwise, all results are cleared.
586# ----------------------------------------------------------------------
587itcl::body Rappture::Analyzer::clear {{xmlobj "all"}} {
588    if {$xmlobj eq "" || $xmlobj eq "all"} {
589        $_resultset clear
590    } else {
591        $_resultset clear $xmlobj
592    }
593
594    # NOTE: Clearing will trigger a !change event on the ResultSet
595    # object, which will trigger calls to _fixResultSet to clean up
596    # the results being displayed.
597}
598
599# ----------------------------------------------------------------------
600# USAGE: download coming
601# USAGE: download controls <downloadCommand>
602# USAGE: download start ?widget?
603# USAGE: download now ?widget?
604#
605# Spools the current result so the user can download it.
606# ----------------------------------------------------------------------
607itcl::body Rappture::Analyzer::download {option args} {
608    set title [$itk_component(viewselector) value]
609    set page [$itk_component(viewselector) translate $title]
610
611    switch -- $option {
612        coming {
613            #
614            # Warn result that a download is coming, in case
615            # it needs to take a screen snap.
616            #
617            if {![regexp {^(|@download|---)$} $page]} {
618                set f [$itk_component(resultpages) page $page]
619                $f.rviewer download coming
620            }
621        }
622        controls {
623            # no controls for this download yet
624            return ""
625        }
626        start {
627            set widget $itk_component(download)
628            if {[llength $args] > 0} {
629                set widget [lindex $args 0]
630                if {[catch {winfo class $widget}]} {
631                    set widget $itk_component(download)
632                }
633            }
634            #
635            # See if this download has any controls.  If so, then
636            # post them now and let the user continue the download
637            # after selecting a file format.
638            #
639            if {$page != ""} {
640                set ext ""
641                set f [$itk_component(resultpages) page $page]
642                set arg [itcl::code $this download now $widget]
643                set popup [$f.rviewer download controls $arg]
644                if {"" != $popup} {
645                    $popup activate $widget below
646                } else {
647                    download now $widget
648                }
649            } else {
650                # this shouldn't happen
651                set file error.html
652                set data "<h1>Not Found</h1>There is no result selected."
653            }
654        }
655        now {
656            set widget $itk_component(download)
657            if {[llength $args] > 0} {
658                set widget [lindex $args 0]
659                if {[catch {winfo class $widget}]} {
660                    set widget $itk_component(download)
661                }
662            }
663            #
664            # Perform the actual download.
665            #
666            if {$page != ""} {
667                set ext ""
668                set f [$itk_component(resultpages) page $page]
669                set item [$itk_component(viewselector) value]
670                set result [$f.rviewer download now $widget $_appName $item]
671                if { $result == "" } {
672                    return;                # User cancelled the download.
673                }
674                foreach {ext data} $result break
675                if {"" == $ext} {
676                    if {"" != $widget} {
677                        Rappture::Tooltip::cue $widget \
678                            "Can't download this result."
679                    }
680                    return
681                }
682                regsub -all {[\ -\/\:-\@\{-\~]} $title {} title
683                set file "$title$ext"
684            } else {
685                # this shouldn't happen
686                set file error.html
687                set data "<h1>Not Found</h1>There is no result selected."
688            }
689
690            Rappture::Logger::log download [$itk_component(viewselector) value]
691            set mesg [Rappture::filexfer::download $data $file]
692            if {[string length $mesg] > 0} {
693                Rappture::Tooltip::cue $widget $mesg
694            }
695        }
696        default {
697            error "bad option \"$option\": should be coming, controls, now, start"
698        }
699    }
700}
701
702# ----------------------------------------------------------------------
703# USAGE: _plot ?<index> <options> <index> <options>...?
704#
705# Used internally to update the plot shown in the current result
706# viewer whenever the resultselector settings have changed.  Causes the
707# desired results to show up on screen.
708# ----------------------------------------------------------------------
709itcl::body Rappture::Analyzer::_plot {args} {
710    #puts "analyzer::_plot"
711    set _plotlist $args
712
713    set page [$itk_component(viewselector) value]
714    set page [$itk_component(viewselector) translate $page]
715    if {"" != $page} {
716        set f [$itk_component(resultpages) page $page]
717        $f.rviewer plot clear
718        foreach {index opts} $_plotlist {
719            $f.rviewer plot add $index $opts
720        }
721    }
722}
723
724# ----------------------------------------------------------------------
725# USAGE: _reorder <compList>
726#
727# Used internally to change the order of a series of output components
728# found in the <output> section.  Moves the <log> elements to the end
729# and returns the updated list.
730# ----------------------------------------------------------------------
731itcl::body Rappture::Analyzer::_reorder {comps} {
732    set i 0
733    set max [llength $comps]
734    while {$i < $max} {
735        set c [lindex $comps $i]
736        if {[string match log* $c]} {
737            set comps [lreplace $comps $i $i]
738            lappend comps $c
739            incr max -1
740        } else {
741            incr i
742        }
743    }
744    return $comps
745}
746
747# ----------------------------------------------------------------------
748# USAGE: _autoLabel <xmlobj> <path> <title> <cntVar>
749#
750# Used internally to check for an about.label property at the <path>
751# in <xmlobj>.  If this object doesn't have a label, then one is
752# supplied using the given <title>.  The <cntVar> is an array of
753# counters in the calling scopes for titles that have been used
754# in the past.  This is used to create titles like "Plot #2" the
755# second time it is encountered.
756#
757# The <xmlobj> is updated so that the label is inserted directly in
758# the tree.
759# ----------------------------------------------------------------------
760itcl::body Rappture::Analyzer::_autoLabel {xmlobj path title cntVar} {
761    upvar $cntVar counters
762
763    set group [$xmlobj get $path.about.group]
764    set label [$xmlobj get $path.about.label]
765    if {"" == $label} {
766        # no label -- make one up using the title specified
767        if {![info exists counters($group-$title)]} {
768            set counters($group-$title) 1
769            set label $title
770        } else {
771            set label "$title (#[incr counters($group-$title)])"
772        }
773        $xmlobj put $path.about.label $label
774    } else {
775        # handle the case of two identical labels in <output>
776        if {![info exists counters($group-$label)]} {
777            set counters($group-$label) 1
778        } else {
779            set label "$label (#[incr counters($group-$label)])"
780            $xmlobj put $path.about.label $label
781        }
782    }
783    return $label
784}
785
786# ----------------------------------------------------------------------
787# USAGE: _fixResult
788#
789# Used internally to change the result page being displayed whenever
790# the user selects a page from the results combobox.
791# ----------------------------------------------------------------------
792itcl::body Rappture::Analyzer::_fixResult {} {
793    set name [$itk_component(viewselector) value]
794    set page ""
795    if {"" != $name} {
796        set page [$itk_component(viewselector) translate $name]
797    }
798    if {$page == "@download"} {
799        # put the combobox back to its last value
800        $itk_component(viewselector) component entry configure -state normal
801        $itk_component(viewselector) component entry delete 0 end
802        $itk_component(viewselector) component entry insert end $_lastlabel
803        $itk_component(viewselector) component entry configure -state disabled
804        # perform the actual download
805        download start $itk_component(download)
806    } elseif {$page == "---"} {
807        # put the combobox back to its last value
808        $itk_component(viewselector) component entry configure -state normal
809        $itk_component(viewselector) component entry delete 0 end
810        $itk_component(viewselector) component entry insert end $_lastlabel
811        $itk_component(viewselector) component entry configure -state disabled
812    } elseif {$page != ""} {
813        set _lastlabel $name
814        $itk_component(resultpages) current $page
815        set f [$itk_component(resultpages) page $page]
816        # We don't want to replot if we're using an existing viewer with the
817        # the same list of objects to plot.  So track the viewer and the list.
818        if { ![info exists _lastPlot($f)] || $_plotlist != $_lastPlot($f) } {
819            set _lastPlot($f) $_plotlist
820            set win [winfo toplevel $itk_component(hull)]
821            blt::busy hold $win
822            #puts "rviewer = $f.rviewer"
823            #puts "_plotlist = $_plotlist"
824            $f.rviewer plot clear
825            eval $f.rviewer plot add $_plotlist
826            blt::busy release $win
827        }
828        Rappture::Logger::log output $_label2item($name)
829        Rappture::Tooltip::for $itk_component(viewselector) \
830            "@[itcl::code $this _resultTooltip]" -log $_label2item($name)
831    }
832}
833
834# ----------------------------------------------------------------------
835# USAGE: _fixResultSet ?<eventData>...?
836#
837# Used internally to react to changes within the ResultSet.  When a
838# result is added, a new result viewer is created for the object.
839# When all results are cleared, the viewers are deleted.
840# ----------------------------------------------------------------------
841itcl::body Rappture::Analyzer::_fixResultSet {args} {
842    #puts "Analyzer::_fixResultSet $args"
843    array set eventData $args
844    switch -- $eventData(op) {
845        add {
846            set xmlobj $eventData(what)
847
848            # Detect molecule elements that contain trajectory data
849            # and convert to sequences.
850            _trajToSequence $xmlobj output
851
852            # Go through the analysis and find all result sets.
853            set haveresults 0
854            foreach item [_reorder [$xmlobj children output]] {
855                if {[$xmlobj get output.$item.about.uqtype] == ""} {
856                    switch -glob -- $item {
857                        log* {
858                            _autoLabel $xmlobj output.$item "Output Log" counters
859                        }
860                        number* {
861                            _autoLabel $xmlobj output.$item "Number" counters
862                        }
863                        integer* {
864                            _autoLabel $xmlobj output.$item "Integer" counters
865                        }
866                        mesh* {
867                            _autoLabel $xmlobj output.$item "Mesh" counters
868                        }
869                        string* {
870                            _autoLabel $xmlobj output.$item "String" counters
871                        }
872                        histogram* - curve* - field* {
873                            _autoLabel $xmlobj output.$item "Plot" counters
874                        }
875                        drawing* {
876                            _autoLabel $xmlobj output.$item "Drawing" counters
877                        }
878                        structure* {
879                            _autoLabel $xmlobj output.$item "Structure" counters
880                        }
881                        table* {
882                            _autoLabel $xmlobj output.$item "Energy Levels" counters
883                        }
884                        sequence* {
885                            _autoLabel $xmlobj output.$item "Sequence" counters
886                        }
887                    }
888                }
889                set label [$xmlobj get output.$item.about.group]
890                if {"" == $label} {
891                    set label [$xmlobj get output.$item.about.label]
892                }
893
894                set hidden [$xmlobj get output.$item.hide]
895                set hidden [expr {"" != $hidden && $hidden}]
896
897                if {"" != $label && !$hidden} {
898                    set haveresults 1
899                }
900            }
901
902            # if there are any valid results, add them to the resultset
903            if {$haveresults} {
904                set index [$_resultset get simnum $xmlobj]
905
906                # add each result to a result viewer
907                foreach item [_reorder [$xmlobj children output]] {
908                    set label [$xmlobj get output.$item.about.group]
909                    if {"" == $label} {
910                        set label [$xmlobj get output.$item.about.label]
911                    }
912                    set hidden [$xmlobj get output.$item.hide]
913                    if {$hidden == ""} {
914                        set hidden 0
915                    }
916                    if {"" != $label && !$hidden} {
917                        set uq_part [$xmlobj get output.$item.about.uqtype]
918
919                        #puts "label=$label uq_part=$uq_part"
920
921                        if {![info exists _label2page($label)]} {
922                            #puts "Adding label: '$label'"
923                            set name "page[incr _pages]"
924                            #puts "Inserting $name into resultpages"
925                            set page [$itk_component(resultpages) \
926                                insert end $name]
927                            set _label2page($label) $page
928                            set _label2item($label) output.$item
929                            set _label2desc($label) \
930                                [$xmlobj get output.$item.about.description]
931                            Rappture::ResultViewer $page.rviewer
932                            pack $page.rviewer -expand yes -fill both -pady 4
933
934                            set end [$itk_component(viewselector) \
935                                choices index -value ---]
936                            if {$end < 0} {
937                                set end "end"
938                            }
939                            $itk_component(viewselector) choices insert $end \
940                                $name $label
941                        }
942
943                        # add/replace the latest result into this viewer
944                        set page $_label2page($label)
945
946                        if {![info exists reset($page)]} {
947                            $page.rviewer clear $index
948                            set reset($page) 1
949                        }
950                        $page.rviewer add $index $xmlobj output.$item $label $uq_part
951                    }
952                }
953            }
954
955            # show the first page by default
956            set max [$itk_component(viewselector) choices size]
957            for {set i 0} {$i < $max} {incr i} {
958                set first [$itk_component(viewselector) choices get -label $i]
959                if {$first != ""} {
960                    set page [$itk_component(viewselector) choices get -value $i]
961                    set char [string index $page 0]
962                    if {$char != "@" && $char != "-"} {
963                        $itk_component(resultpages) current $page
964                        $itk_component(viewselector) value $first
965                        set _lastlabel $first
966                        break
967                    }
968                }
969            }
970        }
971        clear {
972            if {$eventData(what) ne "all"} {
973                # delete this result from all viewers
974                array set params $eventData(what)
975                foreach label [array names _label2page] {
976                    set page $_label2page($label)
977                    $page.rviewer clear $params(simnum)
978                }
979            }
980
981            if {[$_resultset size] == 0} {
982                # reset the size of the controls area
983                set ht [winfo height $itk_component(results)]
984                set cntlht [$itk_component(resultselector) size -controlarea]
985                set frac [expr {double($cntlht)/$ht}]
986                $itk_component(results) fraction end $frac
987
988                foreach label [array names _label2page] {
989                    set page $_label2page($label)
990                    destroy $page.rviewer
991                }
992                $itk_component(resultpages) delete -all
993                set _pages 0
994
995                $itk_component(viewselector) value ""
996                $itk_component(viewselector) choices delete 0 end
997                catch {unset _label2page}
998                catch {unset _label2item}
999                catch {unset _label2desc}
1000                set _plotlist ""
1001
1002                $itk_component(viewselector) choices insert end --- "---"
1003                $itk_component(viewselector) choices insert end \
1004                    @download [Rappture::filexfer::label download]
1005                set _lastlabel ""
1006            }
1007
1008            # fix Simulate button state
1009            reset
1010        }
1011        default {
1012            error "don't know how to handle op \"$eventData(op)\""
1013        }
1014    }
1015}
1016
1017# ----------------------------------------------------------------------
1018# USAGE: _fixSize
1019#
1020# Used internally to change the size of the result set area whenever
1021# a new control appears.  Adjusts the size available for the result
1022# set up to some maximum.
1023# ----------------------------------------------------------------------
1024itcl::body Rappture::Analyzer::_fixSize {} {
1025    set ht [winfo height $itk_component(results)]
1026    if {$ht <= 1} { set ht [winfo reqheight $itk_component(results)] }
1027    set cntlht [$itk_component(resultselector) size -controlarea]
1028    set frac [expr {double($cntlht)/$ht}]
1029
1030    if {$frac < 0.4} {
1031        $itk_component(results) fraction end $frac
1032    }
1033    _fixSimControl
1034}
1035
1036# ----------------------------------------------------------------------
1037# USAGE: _simState <boolean> ?<message>? ?<settings>?
1038#
1039# Used internally to change the "Simulation" button on or off.
1040# If the <boolean> is on, then any <message> and <settings> are
1041# displayed as well.  If the <boolean> is off, then only display
1042# the message. The <message> is a note to the user about
1043# what will be simulated, and the <settings> are a list of
1044# tool parameter settings of the form {path1 val1 path2 val2 ...}.
1045# When these are in place, the next Simulate operation will use
1046# these settings.  This helps fill in missing data values.
1047# ----------------------------------------------------------------------
1048itcl::body Rappture::Analyzer::_simState {state args} {
1049    if {$state} {
1050        $itk_interior.simol configure \
1051            -background $itk_option(-simcontrolactiveoutline)
1052        configure -simcontrolcolor $itk_option(-simcontrolactivebackground)
1053
1054        $itk_component(abort) configure -state disabled
1055        $itk_component(simulate) configure -state normal \
1056            -command [itcl::code $this simulate]
1057
1058        #
1059        # If there's a special message, then put it up next to the button.
1060        #
1061        set mesg [lindex $args 0]
1062        if {"" != $mesg} {
1063            $itk_component(simstatus) configure -state normal
1064            $itk_component(simstatus) delete 1.0 end
1065            $itk_component(simstatus) insert end $mesg
1066
1067            #
1068            # If there are any settings, then install them in the
1069            # "Simulate" button.  Also, pop them up as a tooltip
1070            # for the message.
1071            #
1072            set settings [lindex $args 1]
1073            if {[llength $settings] > 0} {
1074                $itk_component(simulate) configure \
1075                    -command [eval itcl::code $this simulate $settings]
1076
1077                set details ""
1078                foreach {path val} $settings {
1079                    set str [string trim [$_tool xml get $path.about.label]]
1080                    if {"" == $str} {
1081                        set str [$_tool xml element -as id $path]
1082                    }
1083                    append details "$str = $val\n"
1084                }
1085                set details [string trim $details]
1086
1087                Rappture::Tooltip::for $itk_component(simstatus) $details
1088                $itk_component(simstatus) insert end " "
1089                $itk_component(simstatus) insert end "(details...)" popup
1090            }
1091            $itk_component(simstatus) configure -state disabled
1092        }
1093    } else {
1094        if {"" != $itk_option(-simcontrolbackground)} {
1095            set simcbg $itk_option(-simcontrolbackground)
1096        } else {
1097            set simcbg $itk_option(-background)
1098        }
1099        $itk_interior.simol configure \
1100            -background $itk_option(-simcontroloutline)
1101        configure -simcontrolcolor $simcbg
1102
1103        if {$_uq_active == 0} {
1104            $itk_component(simulate) configure -state disabled
1105        }
1106        $itk_component(abort) configure -state normal
1107
1108        $itk_component(simstatus) configure -state normal
1109        $itk_component(simstatus) delete 1.0 end
1110        set mesg [lindex $args 0]
1111        if {"" != $mesg} {
1112            $itk_component(simstatus) insert end $mesg
1113        }
1114        $itk_component(simstatus) configure -state disabled
1115    }
1116}
1117
1118itcl::body Rappture::Analyzer::_simLogError {message} {
1119    puts -nonewline stderr $message
1120}
1121
1122itcl::body Rappture::Analyzer::_simLogOutput {message} {
1123    puts -nonewline stdout $message
1124    flush stdout
1125}
1126
1127# ----------------------------------------------------------------------
1128# USAGE: _simOutput <message>
1129#
1130# Invoked automatically whenever output comes in while running the
1131# tool.  Extracts any =RAPPTURE-???=> messages from the output and
1132# sends the output to the display.  For example, any
1133# =RAPPTURE-PROGRESS=> message pops up the progress meter and updates
1134# it to show the latest progress message.  This is useful for
1135# long-running tools, to let the user know how much longer the
1136# simulation will take.
1137# ----------------------------------------------------------------------
1138itcl::body Rappture::Analyzer::_simOutput {message} {
1139    #
1140    # Scan through and pick out any =RAPPTURE-PROGRESS=> messages first.
1141    #
1142    set pattern {=RAPPTURE-PROGRESS=> *([-+]?[0-9]+) +([^\n]*)(\n|$)}
1143    while {[regexp -indices $pattern $message match percent mesg]} {
1144
1145        foreach {i0 i1} $percent break
1146        set percent [string range $message $i0 $i1]
1147
1148        foreach {i0 i1} $mesg break
1149        set mesg [string range $message $i0 $i1]
1150
1151        pack $itk_component(progress) -fill x -padx 10 -pady 10
1152        $itk_component(progress) settings -percent $percent -message $mesg
1153
1154        foreach {i0 i1} $match break
1155        set message [string replace $message $i0 $i1]
1156    }
1157
1158    #
1159    # Now handle SUBMIT-PROGRESS
1160    #
1161    while {[regexp -indices {=SUBMIT-PROGRESS=> aborted=([0-9]+) finished=([0-9]+) failed=([0-9]+) executing=([0-9]+)\
1162        waiting=([0-9]+) setting_up=([0-9]+) setup=([0-9]+) %done=([0-9.]+) timestamp=([0-9.]+)(\n|$)} $message \
1163        match aborted finished failed executing waiting setting_up setup percent ts mesg]} {
1164
1165        set mesg ""
1166        foreach {i0 i1} $percent break
1167        set percent [string range $message $i0 $i1]
1168        foreach {i0 i1} $failed break
1169        set failed [string range $message $i0 $i1]
1170        foreach {i0 i1} $match break
1171        set message [string replace $message $i0 $i1]
1172
1173        if {$failed != 0} {set mesg "$failed jobs failed!"}
1174        if {$percent >= 100} { set mesg "Jobs finished.  Analyzing results..."}
1175
1176        pack $itk_component(progress) -fill x -padx 10 -pady 10
1177        $itk_component(progress) settings -percent $percent -message $mesg
1178    }
1179
1180    #
1181    # Break up the remaining lines according to =RAPPTURE-ERROR=> messages.
1182    # Show errors in a special color.
1183    #
1184    $itk_component(runinfo) configure -state normal
1185    set pattern {=RAPPTURE-([a-zA-Z]+)=>([^\n]*)(\n|$)}
1186    while {[regexp -indices $pattern $message match type mesg]} {
1187        foreach {i0 i1} $match break
1188        set first [string range $message 0 [expr {$i0-1}]]
1189        if {[string length $first] > 0} {
1190            $itk_component(runinfo) insert end $first
1191            $itk_component(runinfo) insert end \n
1192        }
1193
1194        foreach {t0 t1} $type break
1195        set type [string range $message $t0 $t1]
1196        foreach {m0 m1} $mesg break
1197        set mesg [string range $message $m0 $m1]
1198        if {[string length $mesg] > 0 && $type != "RUN"} {
1199            $itk_component(runinfo) insert end $mesg $type
1200            $itk_component(runinfo) insert end \n $type
1201        }
1202
1203        set message [string range $message [expr {$i1+1}] end]
1204    }
1205
1206    if {[string length $message] > 0} {
1207        $itk_component(runinfo) insert end $message
1208        if {[$itk_component(runinfo) get end-2char] != "\n"} {
1209            $itk_component(runinfo) insert end "\n"
1210        }
1211        $itk_component(runinfo) see end
1212    }
1213    $itk_component(runinfo) configure -state disabled
1214}
1215
1216# ----------------------------------------------------------------------
1217# USAGE: _resultTooltip
1218#
1219# Used internally to build the tooltip string displayed for the
1220# result selector.  If the current page has an associated description,
1221# then it is displayed beneath the result.
1222#
1223# Returns the string for the tooltip.
1224# ----------------------------------------------------------------------
1225itcl::body Rappture::Analyzer::_resultTooltip {} {
1226    set tip ""
1227    set name [$itk_component(viewselector) value]
1228    if {[info exists _label2desc($name)] &&
1229        [string length $_label2desc($name)] > 0} {
1230        append tip "$_label2desc($name)\n\n"
1231    }
1232    if {[array size _label2page] > 1} {
1233        append tip "Use this control to display other output results."
1234    }
1235    return $tip
1236}
1237
1238# ----------------------------------------------------------------------
1239# USAGE: _fixSimControl
1240#
1241# Used internally to add or remove the simulation control at the
1242# top of the analysis area.  This is controlled by the -simcontrol
1243# option.
1244# ----------------------------------------------------------------------
1245itcl::body Rappture::Analyzer::_fixSimControl {} {
1246    switch -- $itk_option(-simcontrol) {
1247        on {
1248            pack $itk_interior.simol -fill x -before $itk_interior.nb
1249        }
1250        off {
1251            pack forget $itk_interior.simol
1252        }
1253        auto {
1254            #
1255            # If we have two or more radiodials, then there is a
1256            # chance of encountering a combination of parameters
1257            # with no data, requiring simulation.
1258            #
1259            if {[$itk_component(resultselector) size -controls] >= 2} {
1260                pack $itk_interior.simol -fill x -before $itk_interior.nb
1261            } else {
1262                pack forget $itk_interior.simol
1263            }
1264        }
1265        default {
1266            error "bad value \"$itk_option(-simcontrol)\": should be on, off, auto"
1267        }
1268    }
1269}
1270
1271# ----------------------------------------------------------------------
1272# USAGE: _fixNotebook
1273#
1274# Used internally to switch the active notebook page
1275# ----------------------------------------------------------------------
1276itcl::body Rappture::Analyzer::_fixNotebook {} {
1277    switch -- $itk_option(-notebookpage) {
1278        about {
1279            $itk_component(notebook) current about
1280        }
1281        simulate {
1282            $itk_component(notebook) current simulate
1283        }
1284        analyze {
1285            $itk_component(notebook) current analyze
1286        }
1287        default {
1288            error "bad value \"$itk_option(-notebookpage)\": should be about, simulate, analyze"
1289        }
1290    }
1291}
1292
1293# ----------------------------------------------------------------------
1294# USAGE: _isPdbTrajectory <data>
1295#
1296# Used internally to determine whether pdb or lammps data represents a
1297# trajectory rather than a single frame
1298# ----------------------------------------------------------------------
1299itcl::body Rappture::Analyzer::_isPdbTrajectory {data} {
1300    if { [llength $data] == 0 } {
1301        return 0
1302    }
1303    set nModels 0
1304    foreach line $data {
1305        if { [string match "MODEL*" $line] }  {
1306            incr nModels
1307            if { $nModels > 1 } {
1308                # Stop if more than one model found.  No need to count them
1309                # all.
1310                return 1
1311            }
1312        }
1313    }
1314    return 0
1315}
1316
1317# ----------------------------------------------------------------------
1318# USAGE: _isLammpsTrajectory <data>
1319#
1320# Used internally to determine whether pdb or lammps data represents a
1321# trajectory rather than a single frame
1322# ----------------------------------------------------------------------
1323itcl::body Rappture::Analyzer::_isLammpsTrajectory { data } {
1324    if { [llength $data] == 0 } {
1325        return 0
1326    }
1327    set nModels 0
1328    foreach line $data {
1329        if { [regexp {^[\t ]*ITEM:[ \t]+TIMESTEP} $line] } {
1330            incr nModels
1331            if { $nModels > 1 } {
1332                # Stop if more than one model found.  No need to count them
1333                # all.
1334                return 1
1335            }
1336        }
1337    }
1338    return 0
1339}
1340
1341# ----------------------------------------------------------------------
1342# USAGE: _pdbToSequence <xmlobj> ?<path>?
1343#
1344# If the molecule element is a trajectory, delete the original
1345# and create a sequence of individual molecules.
1346# Used internally to detect any molecule output elements that contain
1347# trajectory data.  Trajectories will be converted into sequences of
1348# individual molecules.  All other elements will be unaffected. Scans
1349# the entire xml tree if a starting path is not specified.
1350# ----------------------------------------------------------------------
1351itcl::body Rappture::Analyzer::_pdbToSequence {xmlobj path id child data} {
1352
1353    set seqLabel [$xmlobj get ${child}.about.label]
1354    set descr    [$xmlobj get ${child}.about.description]
1355    set formula  [$xmlobj get ${child}.components.molecule.formula]
1356    $xmlobj remove $child
1357
1358    set sequence  $path.sequence($id)
1359    $xmlobj put ${sequence}.about.label $seqLabel
1360    $xmlobj put ${sequence}.about.description $descr
1361    $xmlobj put ${sequence}.index.label "Frame"
1362
1363    set frameNum 0
1364    set numLines [llength $data]
1365    for { set i 0 } { $i < $numLines } { incr i } {
1366        set line [lindex $data $i]
1367        set line [string trim $line]
1368        set contents {}
1369        if { [string match "MODEL*" $line] } {
1370            # Save the contents until we get an ENDMDL record.
1371            for {} { $i < $numLines } { incr i } {
1372                set line [lindex $data $i]
1373                set line [string trim $line]
1374                if { $line == "" } {
1375                    continue;           # Skip blank lines.
1376                }
1377                if { [string match "ENDMDL*" $line] } {
1378                    break;
1379                }
1380                append contents $line\n
1381            }
1382            set frame ${sequence}.element($frameNum)
1383            $xmlobj put ${frame}.index $frameNum
1384
1385            set molecule ${frame}.structure.components.molecule
1386            $xmlobj put ${molecule}.pdb $contents
1387            $xmlobj put ${molecule}.formula $formula
1388            incr frameNum
1389        }
1390    }
1391}
1392
1393# ----------------------------------------------------------------------
1394# USAGE: _lammpsToSequence <xmlobj> ?<path>?
1395#
1396# If the molecule element is a trajectory, delete the original
1397# and create a sequence of individual molecules.
1398# Used internally to detect any molecule output elements that contain
1399# trajectory data.  Trajectories will be converted into sequences of
1400# individual molecules.  All other elements will be unaffected. Scans
1401# the entire xml tree if a starting path is not specified.
1402# ----------------------------------------------------------------------
1403itcl::body Rappture::Analyzer::_lammpsToSequence {xmlobj path id child data} {
1404
1405    set seqLabel [$xmlobj get ${child}.about.label]
1406    set descr    [$xmlobj get ${child}.about.description]
1407    set typemap  [$xmlobj get ${child}.components.molecule.lammpstypemap]
1408    $xmlobj remove $child
1409
1410    set sequence ${path}.sequence($id)
1411    $xmlobj put ${sequence}.about.label $seqLabel
1412    $xmlobj put ${sequence}.about.description $descr
1413    $xmlobj put ${sequence}.index.label "Frame"
1414
1415    set frameNum 0
1416    set frameContents ""
1417    set inModel 0
1418    foreach line $data {
1419        set line [string trim $line]
1420        if { $line == "" } {
1421            continue;                   # Skip blank lines
1422        }
1423        if {[regexp {^[\t ]*ITEM:[ \t]+ATOMS} $line] } {
1424            if { $inModel && $frameContents != "" } {
1425                set frame ${sequence}.element($frameNum)
1426                $xmlobj put ${frame}.index $frameNum
1427
1428                set molecule ${frame}.structure.components.molecule
1429                $xmlobj put ${molecule}.lammps $frameContents
1430                $xmlobj put ${molecule}.lammpstypemap $typemap
1431
1432                incr frameNum
1433                set frameContents ""
1434            }
1435            set inModel 1
1436        } elseif { [scan $line "%d %d %f %f %f" a b c d e] == 5 } {
1437            if { !$inModel } {
1438                puts stderr "found \"$line\" without previous \"ITEM: ATOMS\""
1439                set inModel 1
1440            }
1441            append frameContents $line\n
1442        }
1443    }
1444    if { $frameContents != "" } {
1445        set frame ${sequence}.element($frameNum)
1446        $xmlobj put ${frame}.index $frameNum
1447
1448        set molecule ${frame}.structure.components.molecule
1449        $xmlobj put ${molecule}.lammps $frameContents
1450        $xmlobj put ${molecule}.lammpstypemap $typemap
1451    }
1452}
1453
1454# ----------------------------------------------------------------------
1455# USAGE: _trajToSequence <xmlobj> ?<path>?
1456#
1457#       Check for PDB and LAMMPS trajectories in molecule data and rewrite
1458#       the individual models as a sequence of molecules.  Used internally
1459#       to detect any molecule output elements that contain trajectory data.
1460#       Trajectories will be converted into sequences of individual molecules.
1461#       All other elements will be unaffected. Scans the entire xml tree if a
1462#       starting path is not specified.
1463#
1464# ----------------------------------------------------------------------
1465itcl::body Rappture::Analyzer::_trajToSequence {xmlobj {path ""}} {
1466    # Remove leading dot from path, if present.
1467    if { [string index $path 0] == "." } {
1468        set path [string range $path 1 end]
1469    }
1470    # Otherwise check each child.
1471    foreach child [$xmlobj children $path] {
1472        set current ${path}.${child}
1473        if { [string match "structure*" $child] } {
1474            set isTraj [$xmlobj get ${current}.components.molecule.trajectory]
1475            if { $isTraj == "" || !$isTraj } {
1476                continue;               # Not a trajectory.
1477            }
1478            # Look for trajectory if molecule element found.  Check both pdb
1479            # data and lammps data.
1480            set type [$xmlobj element -as type $current]
1481            set id   [$xmlobj element -as id $current]
1482            set pdbdata    [$xmlobj get ${current}.components.molecule.pdb]
1483            set lammpsdata [$xmlobj get ${current}.components.molecule.lammps]
1484            if { $pdbdata != "" && $lammpsdata != "" } {
1485                puts stderr \
1486                    "found both <pdb> and <lammps> elements: picking pdb"
1487            }
1488            set pdbdata [split $pdbdata \n]
1489            set lammpsdata [split $lammpsdata \n]
1490            if { [_isPdbTrajectory $pdbdata] } {
1491                _pdbToSequence $xmlobj $path $id $current $pdbdata
1492            } elseif { [_isLammpsTrajectory $lammpsdata] } {
1493                _lammpsToSequence $xmlobj $path $id $current $lammpsdata
1494            }
1495            continue
1496        }
1497    }
1498}
1499
1500# ----------------------------------------------------------------------
1501# CONFIGURATION OPTION: -simcontrol
1502#
1503# Controls whether or not the Simulate button is showing.  In some
1504# cases, it is not needed.
1505# ----------------------------------------------------------------------
1506itcl::configbody Rappture::Analyzer::simcontrol {
1507    _fixSimControl
1508}
1509
1510# ----------------------------------------------------------------------
1511# CONFIGURATION OPTION: -notebookpage
1512#
1513# Controls which page of the analyzer notebook is shown. It is
1514# particularly needed when using rerun, when you don't want to
1515# "simulate -ifneeded" because an actual simulation might be
1516# kicked off due to differences between tool.xml and run.xml
1517# ----------------------------------------------------------------------
1518itcl::configbody Rappture::Analyzer::notebookpage {
1519    _fixNotebook
1520}
1521
1522itcl::body Rappture::Analyzer::PostMenu { m } {
1523    if { [FindSimsetsForApp $_appName] == 0 } {
1524        $m entryconfigure "Load Simulations" -state disable
1525        $m entryconfigure "Share Simulations" -state disable
1526        $m entryconfigure "Delete Simulations" -state disabled
1527    } else {
1528        $m entryconfigure "Load Simulations" -state normal
1529        $m entryconfigure "Share Simulations" -state normal
1530        $m entryconfigure "Delete Simulations" -state normal
1531    }
1532    if { [$_resultset size] == 0 } {
1533        $m entryconfigure "Save Simulations" -state disabled
1534    } else {
1535        $m entryconfigure "Save Simulations" -state normal
1536    }
1537}
1538       
1539#
1540# Build Help menu
1541#
1542itcl::body Rappture::Analyzer::buildMenu { m url appName } {
1543    menu $m \
1544        -tearoff 0 \
1545        -postcommand [itcl::code $this PostMenu $m]
1546    if { $appName == "" || $url == "" } {
1547        set state disabled
1548    } else {
1549        set state normal
1550    }
1551    set webcmd Rappture::filexfer::webpage
1552    set group "app-$_appName"
1553    if { [info exists env(SESSION)] }  {
1554        set referer "&referrer=$url/tools/$appName/session?sess=$env(SESSION)"
1555    } else {
1556        set referer ""
1557    }
1558    $m add command -label "About this tool" \
1559        -command [list $webcmd "$url/tools/$appName"] \
1560        -state $state
1561    $m add command -label "Questions?" \
1562        -command [list $webcmd "$url/resources/$_appName/questions"] \
1563        -state $state
1564    $m add command -label "Tickets" -state $state \
1565        -command [list $webcmd "$url/feedback/report_problems?group=$group$referer"]
1566    $m add command -label "Wish List" -state disabled
1567    $m add separator
1568    $m add command -label "Save Simulations" \
1569        -command [itcl::code $this SelectSimsetNameAndSave] \
1570        -state $state
1571    $m add command -label "Load Simulations" \
1572        -command [itcl::code $this SelectSimsetForLoading] \
1573        -state $state
1574    $m add command -label "Delete Simulations" \
1575        -command [itcl::code $this SelectSimsetForDeletion] \
1576        -state $state
1577    $m add command -label "Share Simulations" \
1578        -command [itcl::code $this SelectSimsetToShare] \
1579        -state $state
1580    return $m
1581}
1582
1583itcl::body Rappture::Analyzer::BuildSimulationTable { top } {
1584    # All weirdness is due to the Resultset object.
1585
1586    # First create nodes for each simulation.  The node label is the
1587    # simulation number (such as #1, #2, etc). 
1588    eval $_tree delete 0
1589    set labels "selected simnum"
1590    foreach {name index} [$_resultset diff values simnum] {
1591        set node [$_tree insert 0 -label $name]
1592        $_tree set $node "simnum" $name
1593        set index2name($index) $name
1594       
1595    }
1596    # Next fill in the xmlobj associated with each simulation.  We assume
1597    # that the order is the the same as the simulation number.
1598    set index 0
1599    foreach {xmlobj dummy} [$_resultset diff values xmlobj]  {
1600        set node [$_tree findchild 0 $index2name($index)]
1601        set runfile [$xmlobj get output.filename]
1602        set version [$xmlobj get output.version]
1603        $_tree set $node \
1604            "selected" 1 "xmlobj" $xmlobj "runfile" $runfile "version" $version
1605        incr index
1606    }
1607    # Finally for each different input, for each simulation add the
1608    # label and value for the specific input.
1609    foreach name [$_resultset diff names] {
1610        # Ignore non-input names.
1611        if { $name == "xmlobj" || $name == "simnum" } {
1612            continue
1613        }
1614        foreach node [$_tree children 0] {
1615            set xmlobj [$_tree get $node xmlobj]
1616            set label [$xmlobj get $name.about.label]
1617            set value [$xmlobj get $name.current]
1618            if { [$_tree exists $node $label] } {
1619                puts stderr "This can't be: $label already exists in $node"
1620            }
1621            if { [lsearch $labels $label] < 0 } {
1622                lappend labels $label
1623            }
1624            $_tree set $node $label $value
1625        }
1626    }
1627    frame $top
1628    set tv $top.tv
1629    blt::treeview $top.tv -tree $_tree \
1630        -height 1i \
1631        -xscrollcommand [list $top.xs set] \
1632        -yscrollcommand [list $top.ys set] \
1633        -font "Arial 10"
1634    scrollbar $top.xs -orient horizontal -command "$tv xview"
1635    scrollbar $top.ys -orient vertical -command "$tv yview"
1636       
1637    eval $tv column insert end $labels
1638    $tv style checkbox checkbox -showvalue no
1639    $tv column configure simnum -title "Simulation"
1640    $tv column configure selected -title "Save" -style checkbox \
1641        -width .5i -edit yes
1642    $tv column configure treeView -hide yes
1643    blt::table $top \
1644        0,0 $top.tv -fill both \
1645        0,1 $top.ys -fill y \
1646        1,0 $top.xs -fill x
1647    blt::table configure $top r* c* -resize none
1648    blt::table configure $top r0 c0 -resize both
1649}
1650
1651itcl::body Rappture::Analyzer::CleanName { name } {
1652    regsub -all {/} $name {_} name
1653    return $name
1654}
1655
1656#
1657# Get the results and display them in a table.
1658#
1659# Use the table to omit specific results
1660# Use file chooser to save to specific
1661itcl::body Rappture::Analyzer::SelectSimsetNameAndSave {} {
1662    if { [EditSimset] } {
1663        set name [CleanName $_saved(Name)]
1664        set saveDir [file join ~/data/saved $_appName $name]
1665        set saveDir [file normalize $saveDir]
1666        file mkdir $saveDir
1667        set fileName [file join $saveDir $name.sav]
1668        if { [file exists $fileName] } {
1669            if { ![OverwriteSaveFile] } {
1670                return
1671            }
1672        }
1673        WriteSimsetFile $_appName $fileName 0
1674    }
1675}
1676
1677#
1678# Get the results and display them in a table.
1679#
1680# Use the table to omit specific results
1681# Use file chooser to save to specific
1682itcl::body Rappture::Analyzer::SelectSimsetToShare {} {
1683    if { [GetSimset "share"] } {
1684        global env
1685        set name [CleanName $_saved(Name)]
1686        set shareDir [file join /data/tools/shared $env(USER) $_appName $name]
1687        if { [catch {
1688            CreateSharedPath $shareDir
1689        } errs] != 0 } {
1690            puts stderr errs=$errs
1691            return
1692        }
1693        set fileName [file join $shareDir $name.sav]
1694        if { [file exists $fileName] } {
1695            if { ![OverwriteSaveFile] } {
1696                return
1697            }
1698        }
1699        WriteSimsetFile $_appName $fileName 1
1700        ExportURL $_appName $shareDir
1701    }
1702}
1703
1704#
1705# Checks editor widgets to see if the name and description have been
1706# set.  If this is so it enables the save button in the editor.
1707#
1708itcl::body Rappture::Analyzer::CheckSimsetDetails {} {
1709    set popup .savesimset
1710    if { ![winfo exists $popup] } {
1711        return
1712    }
1713    set inner [$popup component inner]
1714    set name [string trim [$inner.name get]]
1715    set desc [string trim [$inner.desc get 0.0 end]]
1716    $inner.controls.save configure -state disabled
1717    if { $name == "" || $desc == "" } {
1718        return
1719    }
1720    $inner.controls.save configure -state normal
1721}
1722
1723#
1724# Gathers all the information from the simset editor into the _saved array
1725#
1726itcl::body Rappture::Analyzer::SaveSimulations {} {
1727    set popup .savesimset
1728    if { ![winfo exists $popup] } {
1729        return
1730    }
1731    set inner [$popup component inner]
1732    array unset _saved
1733    set name [string trim [$inner.name get]]
1734    set desc [string trim [$inner.desc get 0.0 end]]
1735    set _saved(Application) $_appName
1736    set _saved(Revision) $_revision
1737    set _saved(Date) [clock format [clock seconds]]
1738    set _saved(Name) $name
1739    set _saved(Description) $desc
1740    set files {}
1741    foreach node [$_tree children root] {
1742        set fileName [$_tree get $node runfile]
1743        lappend files $fileName
1744    }
1745    set _saved(Files) $files
1746    set _done 1
1747}
1748
1749itcl::body Rappture::Analyzer::Cancel {} {
1750    set _done 0
1751}
1752
1753itcl::body Rappture::Analyzer::Ok {} {
1754    set popup .selectsimset
1755    if { ![winfo exists $popup] } {
1756        return
1757    }
1758    set inner [$popup component inner]
1759    set tv $inner.tv
1760    if { ![$tv selection present] } {
1761        return
1762    }
1763    set node [$tv curselection]
1764    array unset _saved
1765    array set _saved [$_tree get $node]
1766    set saveDir [file dirname $_saved(FileName)]
1767    set files {}
1768    foreach file $_saved(Files) {
1769        if { [file pathtype $file] == "relative" } {
1770            set file [file join $saveDir $file]
1771        }
1772        lappend files $file
1773    }
1774    set _saved(Files) $files
1775    set _done 1
1776}
1777
1778#
1779# WriteSimsetFile --
1780#
1781#       Write the .sav file and the runfiles in the
1782#            ~/data/saved/$_appName/$name
1783#       directory. 
1784# The run files will be in the same directory as the .sav file. 
1785# For example, if the simset file is /path/to/appName/myName/myName.sav,
1786# the runfile directory is /path/to/appName/myName.
1787#
1788itcl::body Rappture::Analyzer::WriteSimsetFile { appName fileName share } {
1789    set saveDir [file dirname $fileName]
1790    if { [file exists $saveDir] } {
1791        file delete -force $saveDir
1792    }
1793    if { [catch {
1794        if { $share } {
1795            CreateSharedPath $saveDir
1796        } else {
1797            file mkdir $saveDir
1798        }
1799        set f [open $fileName "w"]
1800        puts $f [list "Name"        $_saved(Name)]
1801        puts $f [list "Description" $_saved(Description)]
1802        puts $f [list "Date"        $_saved(Date)]
1803        global env
1804        puts $f [list "Creator"     $env(USER)]
1805        puts $f [list "Application" $_saved(Application)]
1806        puts $f [list "Revision"    $_saved(Revision)]
1807        set runfiles ""
1808        foreach file $_saved(Files) {
1809            set tail [file tail $file]
1810            set dest [file join $saveDir $tail]
1811            if { $share } {
1812                InstallSharedFile $saveDir $file
1813            } else {
1814                file copy -force $file $dest
1815            }
1816            lappend runfiles $tail
1817        }
1818        puts $f [list "Files" $runfiles]
1819        close $f
1820        if { $share } {
1821            file attributes $fileName -permissions o+r,g+rw
1822        }
1823    } errs] != 0 } {
1824        global errorInfo
1825        puts stderr "errs=$errs errorInfo=$errorInfo"
1826    }
1827}
1828
1829itcl::body Rappture::Analyzer::EditSimset {} {
1830    set popup .savesimset
1831    if { [winfo exists $popup] } {
1832        destroy $popup
1833    }
1834    # Create a popup for the print dialog
1835    Rappture::Balloon $popup -title "Save set of simulations..."
1836    set inner [$popup component inner]
1837
1838    label $inner.name_l -text "Simulation Set Name"
1839    entry $inner.name -background white
1840    label $inner.desc_l -text "Simulation Set Description" -height 5
1841    text $inner.desc  -background white
1842    label $inner.select_l -text "Selected Simulations"
1843    BuildSimulationTable $inner.select
1844    frame $inner.controls
1845    bind $inner.desc <KeyPress> [itcl::code $this CheckSimsetDetails]
1846    bind $inner.name <KeyPress> [itcl::code $this CheckSimsetDetails]
1847    bind $inner.desc <Motion> [itcl::code $this CheckSimsetDetails]
1848    bind $inner.name <Motion> [itcl::code $this CheckSimsetDetails]
1849    button $inner.controls.cancel -text "Cancel" \
1850        -command [itcl::code $this Cancel]
1851    button $inner.controls.save -text "Save" \
1852        -command [itcl::code $this SaveSimulations]
1853    blt::table $inner.controls \
1854        0,0 $inner.controls.cancel \
1855        0,1 $inner.controls.save
1856   
1857    blt::table $inner \
1858        0,0 $inner.name_l -anchor ne \
1859        0,1 $inner.name -fill x \
1860        1,0 $inner.desc_l -anchor ne \
1861        1,1 $inner.desc -fill both \
1862        2,0 $inner.select_l -anchor ne \
1863        2,1 $inner.select -fill both \
1864        3,1 $inner.controls  -fill x
1865    blt::table configure $inner r1 -height 1i
1866    blt::table configure $inner r2 -pad 20
1867    $popup configure \
1868        -deactivatecommand [itcl::code $this Cancel]
1869
1870    set inner [$popup component inner]
1871    if {[$_resultset size] > 1} {
1872        blt::table $inner \
1873            2,0 $inner.select_l -anchor ne \
1874            2,1 $inner.select -fill both
1875    } else {
1876        blt::table forget $inner.select $inner.select_l
1877    }
1878    CheckSimsetDetails
1879    update
1880    # Activate the popup and call for the output.
1881    $popup activate $itk_component(help) below
1882    Cancel
1883    tkwait variable [itcl::scope _done]
1884    set doSave $_done
1885    $popup deactivate
1886    return $doSave
1887}
1888
1889
1890itcl::body Rappture::Analyzer::ReadSimsetFile { fileName } {
1891    if { [catch {
1892        set f [open $fileName "r"]
1893        set contents [read $f]
1894        close $f
1895        array set _saved $contents
1896    } errs] != 0 } {
1897        return 0
1898    }
1899    if { ![info exists _saved(Files)] } {
1900        return 0
1901    }
1902    set saveDir [file dirname $fileName]
1903    foreach file $_saved(Files) {
1904        if { [file pathtype $file] == "relative" } {
1905            set file [file join $saveDir $file]
1906        }
1907        if { ![file readable $file] } {
1908            puts stderr "runfile $file isn't readable"
1909            return 0
1910        }
1911    }
1912    set _saved(FileName) $fileName
1913    return 1
1914}
1915
1916#
1917# Run files can be either explicitly named with their absolute or
1918# relative path.  If the path is relative it is assumed the parent
1919# directory is relative to the simset file.
1920#
1921#   /my/path/to/myfile.sav
1922#   /my/path/to/myfile/runfiles...
1923#
1924itcl::body Rappture::Analyzer::LoadSimulations { files } {
1925    set loadobjs {}
1926    set saveDir [file dirname $_saved(FileName)]
1927    foreach runfile $files {
1928        if { [file pathtype $runfile] == "relative" } {
1929            set runfile [file join $saveDir $runfile]
1930        }
1931        if { ![file exists $runfile] } {
1932            puts stderr "can't find run: \"$runfile\""
1933            continue
1934        }
1935        set status [catch {Rappture::library $runfile} result]
1936        lappend loadobjs $result
1937    }
1938    clear
1939    foreach runobj $loadobjs {
1940        load $runobj
1941    }
1942    configure -notebookpage analyze
1943    global win
1944    $win.pager configure -nosim 1
1945    $win.pager current analyzer
1946    $win.pager configure -nosim 0
1947}
1948
1949#
1950# Delete selected simulation set.
1951#
1952itcl::body Rappture::Analyzer::SelectSimsetForDeletion {} {
1953    if { [FindSimsetsForApp $_appName] == 0 } {
1954        return
1955    }
1956    if { [GetSimset "delete"] } {
1957        set saveDir [file dirname $_saved(FileName)]
1958        file delete -force $saveDir
1959    }
1960}
1961
1962#
1963# Find all .sav files for an application and load them into the tree.
1964#
1965itcl::body Rappture::Analyzer::FindSimsetsForApp { appName } {
1966    if { $appName == "" } {
1967        puts stderr "No application name found"
1968        return 0
1969    }
1970    $_tree delete 0
1971    foreach fileName [glob -nocomplain ~/data/saved/$appName/*/*.sav] {
1972        if { [ReadSimsetFile $fileName] } {
1973            if { $_revision > 0 && [info exists _saved(Revision)] &&
1974                 $_revision != $_saved(Revision) } {
1975                continue;               # Revision doesn't match.
1976            }
1977            $_tree insert 0 -label $_saved(Name) -data [array get _saved]
1978        }
1979    }
1980    return [$_tree degree 0]
1981}
1982
1983#
1984# Create dialog to select a simset from the currently available simsets.
1985#
1986itcl::body Rappture::Analyzer::GetSimset { name } {
1987    set popup .selectsimset
1988    if { [winfo exists $popup] } {
1989        destroy $popup
1990    }
1991    # Create a popup for the print dialog
1992    set title [string tolower $name]
1993    Rappture::Balloon $popup -title "Select set of simulations to $title..."
1994    set inner [$popup component inner]
1995   
1996    set tv $inner.tv
1997    blt::treeview $inner.tv -tree $_tree \
1998        -height 1i \
1999        -width 5i \
2000        -xscrollcommand [list $inner.xs set] \
2001        -yscrollcommand [list $inner.ys set] \
2002        -font "Arial 10"
2003    scrollbar $inner.xs -orient horizontal -command "$tv xview"
2004    scrollbar $inner.ys -orient vertical -command "$tv yview"
2005    frame $inner.controls
2006   
2007    $tv column insert end "Name" "Description" "Date" "Creator"
2008    $tv column configure treeView -hide yes
2009    blt::table $inner \
2010        0,0 $inner.tv -fill both \
2011        0,1 $inner.ys -fill y \
2012        1,0 $inner.xs -fill x
2013    blt::table configure $inner r* c* -resize none
2014    blt::table configure $inner r0 c0 -resize both
2015
2016    button $inner.controls.cancel -text "Cancel" \
2017        -command [itcl::code $this Cancel]
2018    set title [string totitle $name]
2019    button $inner.controls.save -text $title \
2020        -command [itcl::code $this Ok]
2021    blt::table $inner.controls \
2022        0,0 $inner.controls.cancel \
2023        0,1 $inner.controls.save
2024   
2025    blt::table $inner \
2026        0,0 $inner.tv -fill both \
2027        0,1 $inner.ys -fill y \
2028        1,0 $inner.xs -fill x \
2029        3,0 $inner.controls  -fill x -cspan 2
2030    blt::table configure $inner r* c* -resize none
2031    blt::table configure $inner r0 c0 -resize both
2032    blt::table configure $inner r2 -height 0.1i
2033   
2034    $popup configure \
2035        -deactivatecommand [itcl::code $this Cancel]
2036
2037    $tv selection set [$_tree firstchild 0]
2038    set inner [$popup component inner]
2039    update
2040    # Activate the popup and call for the output.
2041    $popup activate $itk_component(help) below
2042    Cancel
2043    tkwait variable [itcl::scope _done]
2044    set result $_done
2045    $popup deactivate
2046    return $result
2047}
2048
2049itcl::body Rappture::Analyzer::SelectSimsetForLoading {} {
2050    if { [FindSimsetsForApp $_appName] == 0 } {
2051        return
2052    }
2053    if { [GetSimset "load"] } {
2054        LoadSimulations $_saved(Files)
2055    }
2056}
2057
2058itcl::body Rappture::Analyzer::reload { fileName } {
2059    if { [catch {
2060        ReadSimsetFile $fileName
2061        if { [llength $_saved(Files)] > 0 } {
2062            #StartSplashScreen
2063            LoadSimulations $_saved(Files)
2064            #HideSplashScreen
2065        }
2066    } errs] != 0 } {
2067        puts stderr "can't load \"$fileName\": errs=$errs"
2068    }
2069}
2070
2071itcl::body Rappture::Analyzer::BuildQuestionDialog { popup } {
2072    toplevel $popup -background grey92 -bd 2 -relief raised
2073    wm withdraw $popup
2074    wm overrideredirect $popup true
2075    set inner $popup
2076    # Create the print dialog widget and add it to the the balloon popup.
2077    label $popup.title -text " " \
2078        -padx 10 -pady 10 \
2079        -background grey92
2080    button $popup.yes -text "Yes" \
2081        -highlightthickness 0 \
2082        -bd 2  \
2083        -command [itcl::code $this Save]
2084    button $popup.no -text "No" \
2085        -highlightthickness 0 \
2086        -bd 2 \
2087        -command [itcl::code $this Cancel]
2088    blt::table $popup \
2089        0,0 $popup.title -cspan 2  -fill x -pady 4 \
2090        1,0 $popup.yes -width { 0 1i .6i } -pady 4 \
2091        1,1 $popup.no -width { 0 1i .6i }  -pady 4
2092    blt::table configure $popup  r2 -height 0.125i
2093}
2094
2095itcl::body Rappture::Analyzer::Save {} {
2096    set _done 1
2097}
2098
2099itcl::body Rappture::Analyzer::OverwriteSaveFile {} {
2100    set popup .question
2101    if { ![winfo exists $popup] } {
2102        BuildQuestionDialog $popup
2103    }
2104    set text "Simulation set \"$_saved(Name)\" already exists. Overwrite?"
2105    $popup.title configure -text $text
2106    $popup.yes configure -text "Save"
2107    $popup.no configure -text "Cancel"
2108    set main [winfo toplevel $itk_component(help)]
2109    set pw [winfo reqwidth $popup]
2110    set ph [winfo reqheight $popup]
2111    set sw [winfo reqwidth $main]
2112    set sh [winfo reqheight $main]
2113    set rootx [winfo rootx $main]
2114    set rooty [winfo rooty $main]
2115    set x [expr $rootx + (($sw - $pw) / 2)]
2116    set y [expr $rooty + (($sh - $ph) / 2)]
2117    wm geometry $popup +$x+$y
2118    wm deiconify $popup
2119    update
2120    grab $popup
2121    # Activate the popup and call for the output.
2122    Cancel
2123    tkwait variable [itcl::scope _done]
2124    set doSave $_done
2125    grab release $popup
2126    destroy $popup
2127    return $doSave
2128}
2129
2130itcl::body Rappture::Analyzer::InstallSharedFile { installdir file } {
2131    set dst [file join $installdir [file tail $file]]
2132    file copy -force $file $dst
2133    file attributes $dst -permissions g+rw,o+r
2134    return $dst
2135}
2136
2137itcl::body Rappture::Analyzer::CreateSharedPath { path } {
2138    set dir ""
2139    foreach file [file split $path] {
2140        set dir [file join $dir $file]
2141        if { [file exists $dir] } {
2142            if { ![file isdirectory $dir] } {
2143                error "error in path \"$path\": \"$dir\" is not a directory."
2144            }
2145        } else {
2146            file mkdir $dir
2147            file attributes $dir -permissions g+rwx,o+rx
2148        }
2149    }
2150}
2151
2152itcl::body Rappture::Analyzer::ExportURL { appName installdir } {
2153    set fileName [file join $installdir url.txt]
2154    set name [file tail $installdir]
2155    set f [open $fileName "w"]
2156    puts $f "https://nanohub.org/tools/$appName/invoke?params=file(simset):$installdir/$name.sav\n"
2157    close $f
2158    file attributes $fileName -permissions g+rw,o+r
2159    if { [file exists /usr/bin/exportfile] } {
2160        set msgFile [file join $::Rappture::installdir export.html]
2161        ExportFile $fileName $msgFile
2162    }
2163}
2164
2165itcl::body Rappture::Analyzer::ExportFile { src msgFile } {
2166    set dstdir ~/.filexfer/exportfile
2167    file mkdir $dstdir
2168    set dst [file join $dstdir [file tail $src]]
2169    if { [file exists $dst] } {
2170        puts stderr "could be a problem: already waiting to export $dst"
2171    }
2172    file copy -force $src $dst
2173    # Now export the file.
2174    if { [catch {
2175        global exportFileVar
2176        set exportFileVar 0;            # Kill any previous export.
2177        set mesg "Select file to export to desktop/laptop"
2178        update
2179        set cmd "/usr/bin/exportfile --delete $dst --timeout 3 "
2180        if { [file exists $msgFile] } {
2181            append cmd "--message $msgFile"
2182        }
2183        blt::bgexec exportFileVar /bin/sh -c $cmd &
2184    } errs]  != 0 } {
2185        global errorInfo
2186        puts stderr "err: $errorInfo"
2187        file delete -force $dst
2188    }
2189}
2190
Note: See TracBrowser for help on using the repository browser.