source: branches/1.6/gui/scripts/analyzer.tcl @ 6271

Last change on this file since 6271 was 6271, checked in by gah, 8 years ago

added exportfile to indicate url for shared simulations

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