source: branches/1.4/gui/scripts/loader.tcl @ 6024

Last change on this file since 6024 was 6024, checked in by ldelgass, 9 years ago

whitespace

File size: 18.5 KB
RevLine 
[5679]1# -*- mode: tcl; indent-tabs-mode: nil -*-
[11]2# ----------------------------------------------------------------------
3#  COMPONENT: loader - widget for loading examples and old runs
4#
5#  This widget is a glorified combobox that is used to load various
6#  example files into the application.
7# ======================================================================
8#  AUTHOR:  Michael McLennan, Purdue University
[3177]9#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
[115]10#
11#  See the file "license.terms" for information on usage and
12#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
[11]13# ======================================================================
14package require Itk
15
16option add *Loader.textForeground black widgetDefault
17option add *Loader.textBackground white widgetDefault
18
19itcl::class Rappture::Loader {
20    inherit itk::Widget
21
22    itk_option define -tool tool Tool ""
[437]23    itk_option define -state state State "normal"
[11]24
[22]25    constructor {owner path args} { # defined below }
[640]26    destructor { # defined below }
[11]27
28    public method value {args}
29
30    public method label {}
31    public method tooltip {}
32
33    protected method _newValue {}
[640]34    protected method _uploadValue {path args}
35    protected method _downloadValues {}
[11]36    protected method _tooltip {}
[3186]37    protected method _log {}
[11]38
[5679]39    private method SetDefaultValue { value }
[2241]40
[22]41    private variable _owner ""    ;# thing managing this control
[11]42    private variable _path ""     ;# path in XML to this loader
[201]43    private variable _lastlabel "";# label of last example loaded
[193]44
[640]45    private variable _uppath ""   ;# list: path label desc ...
46    private variable _dnpaths ""  ;# list of download element paths
47    private common _dnpath2state  ;# maps download path => yes/no state
[1225]48    private variable _copyfrom "" ;# copy xml objects from here in example lib
49    private variable _copyto ""   ;# copy xml objects here in example lib
50    private variable _label2file  ;# maps combobox labels to filenames
[11]51}
52
53itk::usual Loader {
54    keep -cursor -font
55    keep -foreground -background
56    keep -textforeground -textbackground
57    keep -selectbackground -selectforeground -selectborderwidth
58}
59
60# ----------------------------------------------------------------------
61# CONSTRUCTOR
62# ----------------------------------------------------------------------
[22]63itcl::body Rappture::Loader::constructor {owner path args} {
64    if {[catch {$owner isa Rappture::ControlOwner} valid] != 0 || !$valid} {
[1694]65        error "bad object \"$owner\": should be Rappture::ControlOwner"
[11]66    }
[22]67    set _owner $owner
[11]68    set _path $path
69
70    itk_component add combo {
[3186]71        Rappture::Combobox $itk_interior.combo -editable no \
72            -interactcommand [itcl::code $this _log]
[11]73    } {
[1694]74        usual
75        keep -width
[11]76    }
77    pack $itk_component(combo) -expand yes -fill both
78    bind $itk_component(combo) <<Value>> [itcl::code $this _newValue]
79
80    eval itk_initialize $args
81
[201]82    # example files are stored here
83    if {$itk_option(-tool) != ""} {
[1694]84        set fdir [$itk_option(-tool) installdir]
[201]85    } else {
[1694]86        set fdir "."
[201]87    }
[3512]88    set defval [string trim [$_owner xml get $path.default]]
[201]89
[11]90    #
[201]91    # If this loader has a <new> section, then create that
[193]92    # entry first.
93    #
[201]94    set newfile ""
95    foreach comp [$_owner xml children -type new $path] {
[3647]96        set name  [string trim [$_owner xml get $path.$comp]]
[1694]97        set fname [file join $fdir examples $name]
[201]98
[1694]99        if {[file exists $fname]} {
100            set newfile $fname
[1263]101
[1694]102            if {[catch {set obj [Rappture::library $fname]} result]} {
103                puts stderr "WARNING: can't load example file \"$fname\""
104                puts stderr "  $result"
105            } else {
106                set label "New"
107                $itk_component(combo) choices insert end $obj $label
[1263]108
[1694]109                # if this is new, add it to the label->file hash
110                if {![info exists entries($label)]} {
111                    set entries($label) $obj
112                    set _label2file($label) [file tail $fname]
113                }
[1263]114
[1694]115                # translate default file name => default label
116                if {[string equal $defval [file tail $fname]]} {
117                    $_owner xml put $path.default "New"
118                }
119            }
120            break
121        } else {
122            puts stderr "WARNING: missing example file \"$fname\""
123        }
[201]124    }
125
126    #
127    # If this loader has an <upload> section, then create that
128    # entry next.
129    #
[193]130    foreach comp [$_owner xml children -type upload $path] {
[1694]131        foreach tcomp [$_owner xml children -type to $path.$comp] {
[3512]132            set topath [string trim [$_owner xml get $path.$comp.$tcomp]]
[1694]133            if { [$_owner xml element -as id $topath] == "" } {
134                puts stderr \
135                    "unknown <upload> path \"$topath\": please fix tool.xml"
136                continue
137            }
[3647]138            set label [string trim [$_owner xml get $topath.about.label]]
139            set desc  [string trim [$_owner xml get $topath.about.description]]
[1694]140            lappend _uppath $topath $label $desc
141        }
142        break
[193]143    }
[640]144    if {[llength $_uppath] > 0} {
[1694]145        $itk_component(combo) choices insert end \
146            @upload [Rappture::filexfer::label upload]
[640]147    }
[193]148
149    #
[201]150    # If this loader has a <download> section, then create that
[640]151    # entry next.  Build a popup for choices if there is more than
152    # one download element.
[201]153    #
[640]154    Rappture::Balloon $itk_component(hull).download \
[1694]155        -title "Choose what to [string tolower [Rappture::filexfer::label downloadWord]]:"
[640]156    set inner [$itk_component(hull).download component inner]
157
158    set i 0
[201]159    foreach comp [$_owner xml children -type download $path] {
[1694]160        foreach dcomp [$_owner xml children -type from $path.$comp] {
[3512]161            set frompath [string trim [$_owner xml get $path.$comp.$dcomp]]
[1694]162            if {"" != $frompath} {
163                lappend _dnpaths $frompath
164                set _dnpath2state($this-$frompath) [expr {$i == 0}]
[640]165
[3647]166                set label [string trim [$_owner xml get $frompath.about.label]]
[1694]167                checkbutton $inner.cb$i -text $label \
168                    -variable ::Rappture::Loader::_dnpath2state($this-$frompath)
169                pack $inner.cb$i -anchor w
170                incr i
171            }
172        }
[201]173    }
[1077]174    button $inner.go -text [Rappture::filexfer::label download] \
[1694]175        -command [itcl::code $this _downloadValues]
[640]176    pack $inner.go -side bottom -padx 50 -pady {4 2}
[201]177
[640]178    if {[llength $_dnpaths] > 0} {
[1694]179        $itk_component(combo) choices insert end \
180            @download [Rappture::filexfer::label download]
[640]181    }
182
[201]183    if {[$itk_component(combo) choices size] > 0} {
[1694]184        $itk_component(combo) choices insert end "---" "---"
[201]185    }
186
187    #
[11]188    # Scan through and extract example objects, and load them into
189    # the combobox.
190    #
[13]191    set flist ""
[22]192    foreach comp [$_owner xml children -type example $path] {
[3512]193        lappend flist [string trim [$_owner xml get $path.$comp]]
[13]194    }
195
196    # if there are no examples, then look for *.xml
197    if {[llength $flist] == 0} {
[1694]198        set flist *.xml
[13]199    }
200
[201]201    catch {unset entries}
[120]202    set _counter 0
[13]203    foreach ftail $flist {
[1694]204        set fpath [file join $fdir examples $ftail]
[50]205
[1694]206        foreach fname [glob -nocomplain $fpath] {
207            if {[string equal $fname $newfile]} {
208                continue
209            }
210            if {[file exists $fname]} {
211                if {[catch {set obj [Rappture::library $fname]} result]} {
212                    puts stderr "WARNING: can't load example file \"$fname\""
213                    puts stderr "  $result"
214                } else {
215                    set label [$obj get about.label]
216                    if {$label == ""} {
217                        set label "Example #[incr _counter]"
218                    }
[11]219
[1694]220                    # if this is new, add it to the label->file hash
221                    if {![info exists entries($label)]} {
222                        set entries($label) $obj
223                        set _label2file($label) [file tail $fname]
224                    }
[17]225
[1694]226                    # translate default file name => default label
227                    if {[string equal $defval [file tail $fname]]} {
228                        $_owner xml put $path.default $label
229                    }
230                }
231            } else {
232                puts stderr "WARNING: missing example file \"$fname\""
233            }
234        }
[11]235    }
[201]236    foreach label [lsort -dictionary [array names entries]] {
[1694]237        $itk_component(combo) choices insert end $entries($label) $label
[201]238    }
[11]239
[3512]240    set _copyfrom [string trim [$_owner xml get $path.copy.from]]
[3647]241    set _copyto   [string trim [$_owner xml get $path.copy.to]]
[1225]242
[11]243    #
244    # Assign the default value to this widget, if there is one.
245    #
[3512]246    set str [string trim [$_owner xml get $path.default]]
[5679]247    if { $str != "" } {
[2744]248        bind $itk_component(hull) <Map> [itcl::code $this SetDefaultValue $str]
[2241]249    }
[11]250}
251
252# ----------------------------------------------------------------------
[640]253# DESTRUCTOR
254# ----------------------------------------------------------------------
255itcl::body Rappture::Loader::destructor {} {
256    # be sure to clean up entries for this widget's download paths
257    foreach path $_dnpaths {
[1694]258        catch {unset _dnpath2state($this-$path)}
[640]259    }
260}
261
262# ----------------------------------------------------------------------
[11]263# USAGE: value ?-check? ?<newval>?
264#
265# Clients use this to query/set the value for this widget.  With
266# no args, it returns the current value for the widget.  If the
267# <newval> is specified, it sets the value of the widget and
268# sends a <<Value>> event.  If the -check flag is included, the
269# new value is not actually applied, but just checked for correctness.
270# ----------------------------------------------------------------------
271itcl::body Rappture::Loader::value {args} {
272    set onlycheck 0
273    set i [lsearch -exact $args -check]
274    if {$i >= 0} {
[1694]275        set onlycheck 1
276        set args [lreplace $args $i $i]
[11]277    }
278
279    if {[llength $args] == 1} {
[1694]280        if {$onlycheck} {
281            # someday we may add validation...
282            return
283        }
284        set newval [lindex $args 0]
285        $itk_component(combo) value $newval
286        return $newval
[11]287
288    } elseif {[llength $args] != 0} {
[1694]289        error "wrong # args: should be \"value ?-check? ?newval?\""
[11]290    }
291
292    #
293    # Query the value and return.
294    #
295    return [$itk_component(combo) value]
296}
297
298# ----------------------------------------------------------------------
299# USAGE: label
300#
301# Clients use this to query the label associated with this widget.
302# Reaches into the XML and pulls out the appropriate label string.
303# ----------------------------------------------------------------------
304itcl::body Rappture::Loader::label {} {
[3513]305    set label [string trim [$_owner xml get $_path.about.label]]
[11]306    if {"" == $label} {
[1694]307        set label "Example"
[11]308    }
309    return $label
310}
311
312# ----------------------------------------------------------------------
313# USAGE: tooltip
314#
315# Clients use this to query the tooltip associated with this widget.
316# Reaches into the XML and pulls out the appropriate description
317# string.  Returns the string that should be used with the
318# Rappture::Tooltip facility.
319# ----------------------------------------------------------------------
320itcl::body Rappture::Loader::tooltip {} {
321    # query tooltip on-demand based on current choice
322    return "@[itcl::code $this _tooltip]"
323}
324
[2241]325#
326# SetDefaultValue --
327#
[2744]328#       Sets the designated default value for the loader.  This must be done
329#       after the entire application is assembled, otherwise the default
330#       values set up by the loader will be overwritten by the various widgets
[5679]331#       themselves when they try to set their default values.
[2241]332#
[2744]333#       This is called from a  <Map> event to the loader (combobox).  This
334#       will get trigger the first time the loader is displayed.  The binding
335#       is then removed.
[2241]336#
337itcl::body Rappture::Loader::SetDefaultValue { value } {
338    after idle [itcl::code $this value $value]
339    # We're done. Remove the binding.
340    bind $itk_component(hull) <Map> {}
341}
342
[11]343# ----------------------------------------------------------------------
344# USAGE: _newValue
345#
346# Invoked automatically whenever the value in the combobox changes.
347# Tries to load the selected example into the tool's data structure.
348# Sends a <<Value>> event to notify clients of the change.
349# ----------------------------------------------------------------------
350itcl::body Rappture::Loader::_newValue {} {
351    set newval [$itk_component(combo) value]
352    set obj [$itk_component(combo) translate $newval]
[193]353    if {$obj == "@upload"} {
[1694]354        set tool [Rappture::Tool::resources -appname]
355        Rappture::filexfer::upload \
356            $tool $_uppath [itcl::code $this _uploadValue]
[201]357
[1694]358        # put the combobox back to its last value
359        $itk_component(combo) component entry configure -state normal
360        $itk_component(combo) component entry delete 0 end
361        $itk_component(combo) component entry insert end $_lastlabel
362        $itk_component(combo) component entry configure -state disabled
[201]363
364    } elseif {$obj == "@download"} {
[1694]365        if {[llength $_dnpaths] == 1} {
366            _downloadValues
367        } else {
368            $itk_component(hull).download activate $itk_component(combo) below
369        }
[201]370
[1694]371        # put the combobox back to its last value
372        $itk_component(combo) component entry configure -state normal
373        $itk_component(combo) component entry delete 0 end
374        $itk_component(combo) component entry insert end $_lastlabel
375        $itk_component(combo) component entry configure -state disabled
[201]376
377    } elseif {$obj == "---"} {
[1694]378        # put the combobox back to its last value
379        $itk_component(combo) component entry configure -state normal
380        $itk_component(combo) component entry delete 0 end
381        $itk_component(combo) component entry insert end $_lastlabel
382        $itk_component(combo) component entry configure -state disabled
[193]383    } elseif {$obj != "" && $itk_option(-tool) != ""} {
[1694]384        if {("" != $_copyfrom) && ("" != $_copyto)} {
385            $obj copy $_copyto from $_copyfrom
386        }
387        $_owner xml put $_path.file $_label2file($newval)
388        $itk_option(-tool) load $obj
389        set _lastlabel $newval
[11]390    }
391
392    event generate $itk_component(hull) <<Value>>
393}
394
395# ----------------------------------------------------------------------
396# USAGE: _tooltip
397#
398# Returns the tooltip for this widget, given the current choice in
399# the selector.  This is normally called by the Rappture::Tooltip
400# facility whenever it is about to pop up a tooltip for this widget.
401# ----------------------------------------------------------------------
402itcl::body Rappture::Loader::_tooltip {} {
[3647]403    set str [string trim [$_owner xml get $_path.about.description]]
[11]404
405    # get the description for the current choice, if there is one
406    set newval [$itk_component(combo) value]
407    set obj [$itk_component(combo) translate $newval]
408    if {$obj != ""} {
[1694]409        if {$obj == "@upload"} {
410            append str "\n\nUse this option to upload data from your desktop."
411        } else {
412            set label [$obj get about.label]
413            if {[string length $label] > 0} {
414                append str "\n\n$label"
415            }
[193]416
[1694]417            set desc [$obj get about.description]
418            if {[string length $desc] > 0} {
419                if {[string length $label] > 0} {
420                    append str ":\n"
421                } else {
[6024]422                    append str "\n\n"
[1694]423                }
424                append str $desc
425            }
426        }
[11]427    }
428    return [string trim $str]
429}
430
431# ----------------------------------------------------------------------
[3186]432# USAGE: _log
433#
434# Used internally to send info to the logging mechanism.  Calls the
435# Rappture::Logger mechanism to log the change to this input.
436# ----------------------------------------------------------------------
437itcl::body Rappture::Loader::_log {} {
438    Rappture::Logger::log input $_path [$itk_component(combo) value]
439}
440
441# ----------------------------------------------------------------------
[681]442# USAGE: _uploadValue ?<key> <value> <key> <value> ...?
[193]443#
444# Invoked automatically whenever the user has uploaded data from
445# the "Upload..." option.  Takes the data value (passed as an
446# argument) and loads into the destination widget.
447# ----------------------------------------------------------------------
[681]448itcl::body Rappture::Loader::_uploadValue {args} {
[213]449    array set data $args
450
[640]451    if {[info exists data(error)]} {
[1694]452        Rappture::Tooltip::cue $itk_component(combo) $data(error)
[640]453    }
454
[681]455    if {[info exists data(path)] && [info exists data(data)]} {
[1694]456        Rappture::Tooltip::cue hide  ;# take down note about the popup window
457        $itk_option(-tool) valuefor $data(path) $data(data)
[640]458
[1694]459        $itk_component(combo) component entry configure -state normal
460        $itk_component(combo) component entry delete 0 end
461        $itk_component(combo) component entry insert end "Uploaded data"
462        $itk_component(combo) component entry configure -state disabled
463        set _lastlabel "Uploaded data"
[640]464    }
465}
466
467# ----------------------------------------------------------------------
468# USAGE: _downloadValues
469#
470# Used internally to download all values checked by the popup that
471# controls downloading.  Sends the values for the various controls
472# out to the user by popping up separate browser windows.
473# ----------------------------------------------------------------------
474itcl::body Rappture::Loader::_downloadValues {} {
475    # take down the popup (in case it was posted)
476    $itk_component(hull).download deactivate
477
478    set mesg ""
[1077]479    foreach path $_dnpaths {
[1694]480        if {$_dnpath2state($this-$path)} {
481            set info [$itk_option(-tool) valuefor $path]
482            set mesg [Rappture::filexfer::download $info input.txt]
483            if {"" != $mesg} { break }
484        }
[213]485    }
486
[640]487    if {"" != $mesg} {
[1694]488        Rappture::Tooltip::cue $itk_component(combo) $mesg
[640]489    }
[193]490}
491
492# ----------------------------------------------------------------------
[11]493# OPTION: -tool
494# ----------------------------------------------------------------------
495itcl::configbody Rappture::Loader::tool {
496    if {[catch {$itk_option(-tool) isa Rappture::Tool} valid] || !$valid} {
[1694]497        error "object \"$itk_option(-tool)\" is not a Rappture Tool"
[11]498    }
499}
[437]500
501# ----------------------------------------------------------------------
502# CONFIGURATION OPTION: -state
503# ----------------------------------------------------------------------
504itcl::configbody Rappture::Loader::state {
505    set valid {normal disabled}
506    if {[lsearch -exact $valid $itk_option(-state)] < 0} {
[1694]507        error "bad value \"$itk_option(-state)\": should be [join $valid {, }]"
[437]508    }
509    $itk_component(combo) configure -state $itk_option(-state)
510}
Note: See TracBrowser for help on using the repository browser.