source: trunk/gui/scripts/controlOwner.tcl @ 1159

Last change on this file since 1159 was 1159, checked in by dkearney, 16 years ago

updated driver to allow the user to revisualize old run files just as rerun did. if driver can find the tool.xml file and where the application is installed, it can also allow the user to run new simulations after preloading old simulations.

Finally figured out where the current tags in group objects were coming from. controlOwner was adjusted to stop putting current tags in things. this only seemed to happen when the application also contained a loader, because we were doing an xml copy of nodes.

rewrote rerun to be a small wrapper around the "driver -load" command. rerun is now a script like rappture, it is populated with rappture's install directory from configure, so configure.in was updated. added some stuff for finding mcc in the configure.in script for a future commit

changed Rappture::Analyzer to accept -notebookpage configure flag so we can specify which page of the analyzer notebook should be currently showing. also inside of the analyzer, changed behavior of simstatus to allow us to still show a message even if the simulation button is disabled. this is helpful because we can tell the user why the simulation button is grayed out. one example of this is the case when we load old run.xml files, but cannot find a tool.xml file. the user cannot run a simulation, even if they change an input, because we can't figure out where the app is installed. now we can tell the user, why they cannot run the simulation.

File size: 17.5 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: owner - manages Rappture controls
3#
4#  This object represents an entity managing Rappture controls.
5#  It is typically a Tool, a DeviceEditor, or some other large entity
6#  that manages a Rappture XML tree.  All controlling widgets are
7#  registered with an owner, and the owner propagates notifications
8#  out to clients who have an interest in a particular control.
9# ======================================================================
10#  AUTHOR:  Michael McLennan, Purdue University
11#  Copyright (c) 2004-2005  Purdue Research Foundation
12#
13#  See the file "license.terms" for information on usage and
14#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15# ======================================================================
16package require Itcl
17
18itcl::class Rappture::ControlOwner {
19    constructor {owner} { # defined below }
20
21    public method xml {args}
22
23    public method load {newobj}
24    public method widgetfor {path args}
25    public method valuefor {path args}
26    public method dependenciesfor {path args}
27    public method ownerfor {path {skip ""}}
28    public method changed {path}
29    public method regularize {path}
30    public method notify {option owner args}
31    public method sync {}
32    public method tool {}
33
34    protected method _slave {option args}
35
36    protected variable _owner ""     ;# ControlOwner containing this one
37    protected variable _path ""      ;# paths within are relative to this
38    protected variable _slaves ""    ;# this owner has these slaves
39    protected variable _xmlobj ""    ;# Rappture XML description
40    private variable _path2widget    ;# maps path => widget on this page
41    private variable _owner2paths    ;# for notify: maps owner => interests
42    private variable _callbacks      ;# for notify: maps owner/path => callback
43    private variable _dependencies   ;# maps path => other paths dep on this
44}
45                                                                               
46# ----------------------------------------------------------------------
47# CONSTRUCTOR
48# ----------------------------------------------------------------------
49itcl::body Rappture::ControlOwner::constructor {owner} {
50    if {"" != $owner} {
51        set parts [split $owner @]
52        set _owner [lindex $parts 0]
53        set _path [lindex $parts 1]
54        $_owner _slave add $this
55    }
56}
57
58# ----------------------------------------------------------------------
59# USAGE: xml <subcommand> ?<arg> <arg> ...?
60# USAGE: xml object
61#
62# Used by clients to manipulate the underlying XML data for this
63# tool.  The <subcommand> can be any operation supported by a
64# Rappture::library object.  Clients can also request the XML object
65# directly by using the "object" subcommand.
66# ----------------------------------------------------------------------
67itcl::body Rappture::ControlOwner::xml {args} {
68    if {"object" == $args} {
69        return $_xmlobj
70    }
71    return [eval $_xmlobj $args]
72}
73
74# ----------------------------------------------------------------------
75# USAGE: widgetfor <path> ?<widget>?
76#
77# Used by embedded widgets such as a Controls panel to register the
78# various controls associated with this page.  That way, this
79# ControlOwner knows what widgets to look at when syncing itself
80# to the underlying XML data.
81# ----------------------------------------------------------------------
82itcl::body Rappture::ControlOwner::widgetfor {path args} {
83    # if this is a query operation, then look for the path
84    if {[llength $args] == 0} {
85        set owner [ownerfor $path]
86        if {$owner != $this && $owner != ""} {
87            return [$owner widgetfor $path]
88        }
89        if {[info exists _path2widget($path)]} {
90            return $_path2widget($path)
91        }
92        return ""
93    }
94
95    # otherwise, associate the path with the given widget
96    set widget [lindex $args 0]
97    if {"" != $widget} {
98        if {[info exists _path2widget($path)]} {
99            error "$path already associated with widget $_path2widget($path)"
100        }
101        set _path2widget($path) $widget
102    } else {
103        catch {unset _path2widget($path)}
104    }
105}
106
107# ----------------------------------------------------------------------
108# USAGE: valuefor <path> ?<newValue>?
109#
110# Used by embedded widgets such as a Loader to query or set the
111# value of another control.  With no extra args, it returns the
112# value of the widget at the <path> in the XML.  Otherwise, it
113# sets the value of the widget to <newValue>.
114# ----------------------------------------------------------------------
115itcl::body Rappture::ControlOwner::valuefor {path args} {
116    set owner [ownerfor $path]
117
118    # if this is a query operation, then look for the path
119    if {[llength $args] == 0} {
120        if {$owner != $this && $owner != ""} {
121            return [$owner valuefor $path]
122        }
123        if {[info exists _path2widget($path)]} {
124            return [$_path2widget($path) value]
125        }
126        # can't find the path? try removing the prefix for this owner
127        set plen [string length $_path]
128        if {[string equal -length $plen $_path $path]} {
129            set relpath [string range $path [expr {$plen+1}] end]
130            if {[info exists _path2widget($relpath)]} {
131                return [$_path2widget($relpath) value]
132            }
133        }
134        return ""
135    }
136
137    # otherwise, set the value
138    if {$owner != $this && $owner != ""} {
139        return [eval $owner valuefor $path $args]
140    }
141    if {[llength $args] > 1} {
142        error "wrong # args: should be \"valuefor path ?newValue?\""
143    }
144
145    if {[info exists _path2widget($path)]} {
146        $_path2widget($path) value [lindex $args 0]
147    } else {
148        error "bad path \"$path\": should be one of [join [lsort [array names _path2widget]] {, }]"
149    }
150}
151
152# ----------------------------------------------------------------------
153# USAGE: dependenciesfor <path> ?<path>...?
154#
155# Used by embedded widgets such as a Controls panel to register the
156# various controls that are dependent on another one.  If only one
157# path is specified, then this method returns all known dependencies
158# for the specified <path>.  Otherwise, the additional <path>'s are
159# noted as being dependent on the first <path>.
160# ----------------------------------------------------------------------
161itcl::body Rappture::ControlOwner::dependenciesfor {path args} {
162    if {"" != $_owner} {
163        #
164        # Keep all dependencies at the highest level.
165        # That way, a structure can come and go, but the
166        # dependencies remain fixed in the topmost tool.
167        #
168        set plen [string length $_path]
169        if {"" != $_path && ![string equal -length $plen $_path $path]} {
170            set path $_path.$path
171        }
172        return [eval $_owner dependenciesfor $path $args]
173    }
174
175    # if this is a query operation, then look for the path
176    if {[llength $args] == 0} {
177        if {[info exists _dependencies($path)]} {
178            return $_dependencies($path)
179        }
180        return ""
181    }
182
183    # add new dependencies
184    if {![info exists _dependencies($path)]} {
185        set _dependencies($path) ""
186    }
187    foreach dpath $args {
188        set i [lsearch -exact $_dependencies($path) $dpath]
189        if {$i < 0} {
190            lappend _dependencies($path) $dpath
191        }
192    }
193}
194
195# ----------------------------------------------------------------------
196# USAGE: ownerfor <path> ?<skip>?
197#
198# Returns the ControlOwner that directly controls the specified <path>.
199# ----------------------------------------------------------------------
200itcl::body Rappture::ControlOwner::ownerfor {path {skip ""}} {
201    if {[info exists _path2widget($path)]} {
202        return $this
203    }
204
205    # can't find the path? try removing the prefix for this owner
206    set plen [string length $_path]
207    if {[string equal -length $plen $_path $path]} {
208        set relpath [string range $path [expr {$plen+1}] end]
209        if {[info exists _path2widget($relpath)]} {
210            return $this
211        }
212    }
213
214    # couldn't find this path?  then check all subordinates
215    foreach slave $_slaves {
216        if {$slave == $skip} {
217            continue  ;# skip this slave if it's already been searched
218        }
219        set rval [$slave ownerfor $path $this]
220        if {"" != $rval} {
221            return $rval
222        }
223    }
224
225    # check the owner as a last resort
226    if {"" != $_owner && $_owner != $skip} {
227        set rval [$_owner ownerfor $path $this]
228        if {"" != $rval} {
229            return $rval
230        }
231    }
232
233    return ""
234}
235
236# ----------------------------------------------------------------------
237# USAGE: load <xmlobj>
238#
239# Loads the contents of a Rappture <xmlobj> into the controls
240# associated with this tool.
241# ----------------------------------------------------------------------
242itcl::body Rappture::ControlOwner::load {newobj} {
243    if {![Rappture::library isvalid $newobj]} {
244        error "\"$newobj\" is not a Rappture::library"
245    }
246
247    foreach path [array names _path2widget] {
248        # the following elements do not accept "current" tags, skip them
249        set type [[tool] xml element -as type $path]
250        if {[lsearch {group separator control note} $type] >= 0} {
251            continue
252        }
253
254        # copy new value to the XML tree
255        [tool] xml copy $path.current from $newobj $path.current
256
257        # also copy to the widget associated with the tree
258        if {"" != [$newobj element -as type $path.current]} {
259            set val [$newobj get $path.current]
260            if {[string length $val] > 0
261                  || [llength [$newobj children $path.current]] == 0} {
262                $_path2widget($path) value $val
263            } else {
264                set obj [$newobj element -as object $path.current]
265                $_path2widget($path) value $obj
266            }
267        }
268    }
269}
270
271# ----------------------------------------------------------------------
272# USAGE: changed <path>
273#
274# Invoked automatically by the various widgets associated with this
275# tool whenever their value changes.  Sends notifications to any
276# client that has registered an interest via "notify add".
277# ----------------------------------------------------------------------
278itcl::body Rappture::ControlOwner::changed {path} {
279    if {"" != $_owner} {
280        set plen [string length $_path]
281        if {"" != $_path && ![string equal -length $plen $_path $path]} {
282            set path $_path.$path
283        }
284        $_owner changed $path
285    } else {
286        # send out any callback notifications
287        foreach owner [array names _owner2paths] {
288            foreach pattern $_owner2paths($owner) {
289                if {[string match $pattern $path]} {
290                    uplevel #0 $_callbacks($owner/$pattern)
291                    break
292                }
293            }
294        }
295
296        # find the control panel for each dependency, and tell it
297        # to update its layout.
298        foreach cpath [dependenciesfor $path] {
299            set wv [widgetfor $cpath]
300            while {"" != $wv} {
301                set wv [winfo parent $wv]
302                if {[winfo class $wv] == "Controls"} {
303                    $wv refresh
304                    break
305                }
306            }
307        }
308    }
309}
310
311# ----------------------------------------------------------------------
312# USAGE: regularize <path>
313#
314# Clients use this to get a full, regularized path for the specified
315# <path>, which may be relative to the current owner.
316# ----------------------------------------------------------------------
317itcl::body Rappture::ControlOwner::regularize {path} {
318    set owner [ownerfor $path]
319    if {$owner != $this && $owner != ""} {
320        return [$owner regularize $path]
321    }
322    set rpath ""
323    if {"" != $_xmlobj} {
324        set rpath [$_xmlobj element -as path $path]
325
326        # can't find the path? try removing the prefix for this owner
327        if {"" == $rpath} {
328            set plen [string length $_path]
329            if {[string equal -length $plen $_path $path]} {
330                set relpath [string range $path [expr {$plen+2}] end]
331                set rpath [$_xmlobj element -as path $relpath]
332            }
333        }
334
335        if {"" != $rpath && "" != $_path} {
336            # return a full name for the path
337            set rpath "$_path.$rpath"
338        }
339    }
340    return $rpath
341}
342
343# ----------------------------------------------------------------------
344# USAGE: notify add <owner> <path> <callback>
345# USAGE: notify info ?<owner>? ?<path>?
346# USAGE: notify remove <owner> ?<path> ...?
347#
348# Clients use this to request notifications about changes to a
349# particular <path> for a control under this tool.  Whenever the
350# value associated with <path> changes, the client identified by
351# <owner> is sent a message by invoking its <callback> routine.
352#
353# Notifications can be silenced by calling the "notify remove"
354# function.
355# ----------------------------------------------------------------------
356itcl::body Rappture::ControlOwner::notify {option args} {
357    switch -- $option {
358        add {
359            if {[llength $args] != 3} {
360                error "wrong # args: should be \"notify add owner path callback\""
361            }
362            set owner [lindex $args 0]
363            set path [lindex $args 1]
364            set cb [lindex $args 2]
365
366            if {[info exists _owner2paths($owner)]} {
367                set plist $_owner2paths($owner)
368            } else {
369                set plist ""
370            }
371
372            set i [lsearch -exact $plist $path]
373            if {$i < 0} { lappend _owner2paths($owner) $path }
374            set _callbacks($owner/$path) $cb
375        }
376        info {
377            if {[llength $args] == 0} {
378                # no args? then return all owners
379                return [array names _owner2paths]
380            } else {
381                set owner [lindex $args 0]
382                if {[info exists _owner2paths($owner)]} {
383                    set plist $_owner2paths($owner)
384                } else {
385                    set plist ""
386                }
387                if {[llength $args] == 1} {
388                    # 1 arg? then return paths for this owner
389                    return $plist
390                } elseif {[llength $args] == 2} {
391                    # 2 args? then return callback for this path
392                    set path [lindex $args 1]
393                    if {[info exists _callbacks($owner/$path)]} {
394                        return $_callbacks($owner/$path)
395                    }
396                    return ""
397                } else {
398                    error "wrong # args: should be \"notify info ?owner? ?path?\""
399                }
400            }
401        }
402        remove {
403            if {[llength $args] < 1} {
404                error "wrong # args: should be \"notify remove owner ?path ...?\""
405            }
406            set owner [lindex $args 0]
407
408            if {[llength $args] == 1} {
409                # no args? then delete all paths for this owner
410                if {[info exists _owner2paths($owner)]} {
411                    set plist $_owner2paths($owner)
412                } else {
413                    set plist ""
414                }
415            } else {
416                set plist [lrange $args 1 end]
417            }
418
419            # forget about the callback for each path
420            foreach path $plist {
421                catch {unset _callbacks($owner/$path)}
422
423                if {[info exists _owner2paths($owner)]} {
424                    set i [lsearch -exact $_owner2paths($owner) $path]
425                    if {$i >= 0} {
426                        set _owner2paths($owner) \
427                            [lreplace $_owner2paths($owner) $i $i]
428                    }
429                }
430            }
431        }
432    }
433}
434
435# ----------------------------------------------------------------------
436# USAGE: sync
437#
438# Used by descendents such as a Controls panel to register the
439# various controls associated with this page.  That way, this
440# ControlOwner knows what widgets to look at when syncing itself
441# to the underlying XML data.
442# ----------------------------------------------------------------------
443itcl::body Rappture::ControlOwner::sync {} {
444    # sync all of the widgets under control of this owner
445    if {"" != $_xmlobj} {
446        foreach path [lsort [array names _path2widget]] {
447            set type [$_xmlobj element -as type $path]
448            if {[lsearch {group separator control note} $type] >= 0} {
449                continue
450            }
451            $_xmlobj put $path.current [$_path2widget($path) value]
452        }
453    }
454
455    # sync all subordinate slaves as well
456    foreach slave $_slaves {
457        $slave sync
458    }
459}
460
461# ----------------------------------------------------------------------
462# USAGE: tool
463#
464# Clients use this to figure out which tool is associated with
465# this object.  If there is no parent ControlOwner, then this
466# must be the top-level tool.
467# ----------------------------------------------------------------------
468itcl::body Rappture::ControlOwner::tool {} {
469    if {"" != $_owner} {
470        return [$_owner tool]
471    }
472    return $this
473}
474
475# ----------------------------------------------------------------------
476# USAGE: _slave add <newobj>...
477#
478# Used internally to register the parent-child relationship whenever
479# one ControlOwner is registered to another.  When the parent syncs,
480# it causes all of its children to sync.  When a name is being
481# resolved, it is resolved locally first, then up to the parent for
482# further resolution.
483# ----------------------------------------------------------------------
484itcl::body Rappture::ControlOwner::_slave {option args} {
485    switch -- $option {
486        add {
487            eval lappend _slaves $args
488        }
489        default {
490            error "bad option \"$option\": should be add"
491        }
492    }
493}
Note: See TracBrowser for help on using the repository browser.