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

Last change on this file since 6480 was 6473, checked in by ldelgass, 8 years ago

Merge simsets, result cache (instant-on) from release branches.

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