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

Last change on this file since 724 was 724, checked in by mmc, 17 years ago

Fixed the bugreport component to automatically register all "Oops!"
errors with the ticketing system on the hosting hub. When the
RAPPTURE_VERSION environment variable is set to "current", the
dialog automatically registers errors. Otherwise, it pops up a
dialog showing the error to the developer. It also registers errors
that come from launching jobs ("Problem launching job" messages).

Fixed the Makefile to install all scripts/*.tcl files, so we don't
have to modify configure each time a file is added.

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 @upload "Upload..."
124    }
125
126    #
127    # If this loader has a <download> section, then create that
128    # entry next.  Build a popup for choices if there is more than
129    # one download element.
130    #
131    Rappture::Balloon $itk_component(hull).download \
132        -title "Choose what to download:"
133    set inner [$itk_component(hull).download component inner]
134
135    set i 0
136    foreach comp [$_owner xml children -type download $path] {
137        foreach dcomp [$_owner xml children -type from $path.$comp] {
138            set frompath [$_owner xml get $path.$comp.$dcomp]
139            if {"" != $frompath} {
140                lappend _dnpaths $frompath
141                set _dnpath2state($this-$frompath) [expr {$i == 0}]
142
143                set label [$_owner xml get $frompath.about.label]
144                checkbutton $inner.cb$i -text $label \
145                    -variable ::Rappture::Loader::_dnpath2state($this-$frompath)
146                pack $inner.cb$i -anchor w
147                incr i
148            }
149        }
150    }
151    button $inner.go -text "Download" \
152        -command [itcl::code $this _downloadValues]
153    pack $inner.go -side bottom -padx 50 -pady {4 2}
154
155    if {[llength $_dnpaths] > 0} {
156        $itk_component(combo) choices insert end @download "Download..."
157    }
158
159    if {[$itk_component(combo) choices size] > 0} {
160        $itk_component(combo) choices insert end "---" "---"
161    }
162
163    #
164    # Scan through and extract example objects, and load them into
165    # the combobox.
166    #
167    set flist ""
168    foreach comp [$_owner xml children -type example $path] {
169        lappend flist [$_owner xml get $path.$comp]
170    }
171
172    # if there are no examples, then look for *.xml
173    if {[llength $flist] == 0} {
174        set flist *.xml
175    }
176
177    catch {unset entries}
178    set _counter 0
179    foreach ftail $flist {
180        set fpath [file join $fdir examples $ftail]
181
182        foreach fname [glob -nocomplain $fpath] {
183            if {[string equal $fname $newfile]} {
184                continue
185            }
186            if {[file exists $fname]} {
187                if {[catch {set obj [Rappture::library $fname]} result]} {
188                    puts stderr "WARNING: can't load example file \"$fname\""
189                    puts stderr "  $result"
190                } else {
191                    set label [$obj get about.label]
192                    if {$label == ""} {
193                        set label "Example #[incr _counter]"
194                    }
195
196                    # if this is new, add it
197                    if {![info exists entries($label)]} {
198                        set entries($label) $obj
199                    }
200
201                    # translate default file name => default label
202                    if {[string equal $defval [file tail $fname]]} {
203                        $_owner xml put $path.default $label
204                    }
205                }
206            } else {
207                puts stderr "WARNING: missing example file \"$fname\""
208            }
209        }
210    }
211    foreach label [lsort -dictionary [array names entries]] {
212        $itk_component(combo) choices insert end $entries($label) $label
213    }
214
215    #
216    # Assign the default value to this widget, if there is one.
217    #
218    set str [$_owner xml get $path.default]
219    if {$str != ""} { after 1000 [itcl::code $this value $str] }
220}
221
222# ----------------------------------------------------------------------
223# DESTRUCTOR
224# ----------------------------------------------------------------------
225itcl::body Rappture::Loader::destructor {} {
226    # be sure to clean up entries for this widget's download paths
227    foreach path $_dnpaths {
228        catch {unset _dnpath2state($this-$path)}
229    }
230}
231
232# ----------------------------------------------------------------------
233# USAGE: value ?-check? ?<newval>?
234#
235# Clients use this to query/set the value for this widget.  With
236# no args, it returns the current value for the widget.  If the
237# <newval> is specified, it sets the value of the widget and
238# sends a <<Value>> event.  If the -check flag is included, the
239# new value is not actually applied, but just checked for correctness.
240# ----------------------------------------------------------------------
241itcl::body Rappture::Loader::value {args} {
242    set onlycheck 0
243    set i [lsearch -exact $args -check]
244    if {$i >= 0} {
245        set onlycheck 1
246        set args [lreplace $args $i $i]
247    }
248
249    if {[llength $args] == 1} {
250        if {$onlycheck} {
251            # someday we may add validation...
252            return
253        }
254        set newval [lindex $args 0]
255        $itk_component(combo) value $newval
256        return $newval
257
258    } elseif {[llength $args] != 0} {
259        error "wrong # args: should be \"value ?-check? ?newval?\""
260    }
261
262    #
263    # Query the value and return.
264    #
265    return [$itk_component(combo) value]
266}
267
268# ----------------------------------------------------------------------
269# USAGE: label
270#
271# Clients use this to query the label associated with this widget.
272# Reaches into the XML and pulls out the appropriate label string.
273# ----------------------------------------------------------------------
274itcl::body Rappture::Loader::label {} {
275    set label [$_owner xml get $_path.about.label]
276    if {"" == $label} {
277        set label "Example"
278    }
279    return $label
280}
281
282# ----------------------------------------------------------------------
283# USAGE: tooltip
284#
285# Clients use this to query the tooltip associated with this widget.
286# Reaches into the XML and pulls out the appropriate description
287# string.  Returns the string that should be used with the
288# Rappture::Tooltip facility.
289# ----------------------------------------------------------------------
290itcl::body Rappture::Loader::tooltip {} {
291    # query tooltip on-demand based on current choice
292    return "@[itcl::code $this _tooltip]"
293}
294
295# ----------------------------------------------------------------------
296# USAGE: _newValue
297#
298# Invoked automatically whenever the value in the combobox changes.
299# Tries to load the selected example into the tool's data structure.
300# Sends a <<Value>> event to notify clients of the change.
301# ----------------------------------------------------------------------
302itcl::body Rappture::Loader::_newValue {} {
303    set newval [$itk_component(combo) value]
304    set obj [$itk_component(combo) translate $newval]
305    if {$obj == "@upload"} {
306        if {[Rappture::filexfer::enabled]} {
307            set tool [Rappture::Tool::resources -appname]
308            Rappture::filexfer::upload \
309                $tool $_uppath [itcl::code $this _uploadValue]
310        }
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    if {[Rappture::filexfer::enabled]} {
420        foreach path $_dnpaths {
421            if {$_dnpath2state($this-$path)} {
422                set info [$itk_option(-tool) valuefor $path]
423                set mesg [Rappture::filexfer::download $info input.txt]
424                if {"" != $mesg} { break }
425            }
426        }
427    }
428
429    if {"" != $mesg} {
430        Rappture::Tooltip::cue $itk_component(combo) $mesg
431    }
432}
433
434# ----------------------------------------------------------------------
435# OPTION: -tool
436# ----------------------------------------------------------------------
437itcl::configbody Rappture::Loader::tool {
438    if {[catch {$itk_option(-tool) isa Rappture::Tool} valid] || !$valid} {
439        error "object \"$itk_option(-tool)\" is not a Rappture Tool"
440    }
441}
442
443# ----------------------------------------------------------------------
444# CONFIGURATION OPTION: -state
445# ----------------------------------------------------------------------
446itcl::configbody Rappture::Loader::state {
447    set valid {normal disabled}
448    if {[lsearch -exact $valid $itk_option(-state)] < 0} {
449        error "bad value \"$itk_option(-state)\": should be [join $valid {, }]"
450    }
451    $itk_component(combo) configure -state $itk_option(-state)
452}
Note: See TracBrowser for help on using the repository browser.