source: trunk/gui/scripts/loader.tcl @ 1128

Last change on this file since 1128 was 1077, checked in by mmc, 16 years ago

Fixed the Rappture::filexfer facility so that if importfile/exportfile
commands are not available, it reverts to local Load/Save? operations.
This is important for applications that are not deployed in a hub, but
used instead in a standard desktop environment.

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