source: trunk/gui/scripts/dispatcher.tcl @ 46

Last change on this file since 46 was 11, checked in by mmc, 19 years ago

Major reorganization of the entire package. The config.xml file
is now irrelevant. All the action is in the tool.xml file. The
main program now organizes all input into 1) side-by-side pages,
2) input/result (wizard-style) pages, or 3) a series of wizard-
style pages. The <input> can have <phase> parts representing
the various pages.

Added a new ContourResult? widget based on Swaroop's vtk plotting
code.

Also, added easymesh and showmesh to the "tools" directory.
We need these for Eric Polizzi's code.

File size: 11.7 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: dispatcher - central notification mechanism
3#
4#  The DispatchObj is used within other objects to manage events that
5#  the object sends to itself or other clients.  Each event type
6#  must first be registered via the "register" method.  After that,
7#  an event can be dispatched by calling the "event" method.  Clients
8#  can bind to the various event types by registering a callback via
9#  the "dispatch" method.
10#
11#  The Dispatcher base class provides a foundation for objects that
12#  use the DispatchObj internally.
13# ======================================================================
14#  AUTHOR:  Michael McLennan, Purdue University
15#  Copyright (c) 2004-2005
16#  Purdue Research Foundation, West Lafayette, IN
17# ======================================================================
18package require Itcl
19
20namespace eval Rappture { # forward declaration }
21
22proc Rappture::dispatcher {varName} {
23    upvar $varName obj
24    set obj [Rappture::DispatchObj ::#auto]
25    trace variable obj u "[list rename $obj {}]; list"
26}
27
28itcl::class Rappture::DispatchObj {
29    constructor {} { # defined below }
30    destructor { # defined below }
31
32    public method register {args}
33    public method dispatch {option args}
34    public method event {name args}
35    public method cancel {args}
36    public method ispending {name}
37
38    protected method _send {event caller {arglist ""}}
39
40    private variable _event2clients  ;# maps !event => clients
41    private variable _event2datacb   ;# maps !event => data callback
42    private variable _dispatch       ;# maps !event/client => callback
43    private variable _extraargs      ;# extra args for dispatch calls
44}
45                                                                               
46# ----------------------------------------------------------------------
47# CONSTRUCTOR
48# ----------------------------------------------------------------------
49itcl::body Rappture::DispatchObj::constructor {} {
50    # nothing to do
51}
52                                                                               
53# ----------------------------------------------------------------------
54# DESTRUCTOR
55# ----------------------------------------------------------------------
56itcl::body Rappture::DispatchObj::destructor {} {
57    eval cancel [array names _event2clients]
58}
59
60# ----------------------------------------------------------------------
61# USAGE: register !event ?dataCallback?
62#
63# Clients use this to register new event types with this dispatcher.
64# Once registered, each !event name can be used in the "dispatch"
65# method to bind to the event, and in the "event" method to raise
66# the event.  If the optional dataCallback is supplied, then it is
67# invoked whenever the event is triggered to generate event data
68# that gets passed along to the various dispatch callbacks.
69# ----------------------------------------------------------------------
70itcl::body Rappture::DispatchObj::register {name {datacb ""}} {
71    if {![regexp {^![-_a-zA-Z0-9]} $name]} {
72        error "bad name \"$name\": should be !name"
73    }
74    if {[info exists _event2clients($name)]} {
75        error "event \"$name\" already exists"
76    }
77    set _event2clients($name) ""
78    set _event2datacb($name) $datacb
79}
80
81# ----------------------------------------------------------------------
82# USAGE: dispatch caller ?-now? ?!event ...? ?callback?
83#
84# Clients use this to query binding information, or to bind a callback
85# to one or more of the !events recognized by this object.
86#
87# With no args, this returns a list of all events that are currently
88# bound to the caller.
89#
90# With one argument, this returns the callback associated with the
91# specified !event for the given caller.
92#
93# Otherwise, it sets the callback for one or more events.  If the
94# -now flag is specified, then the callback is invoked immediately
95# to send data back to this caller.  This comes in handy when the
96# caller is set up to react to changes.  It provides a way of
97# initializing the client, as if an event had just occurred.
98# ----------------------------------------------------------------------
99itcl::body Rappture::DispatchObj::dispatch {caller args} {
100    if {[llength $args] == 0} {
101        # no args? then return a list of events for this caller
102        foreach key [array names _dispatch $caller-*] {
103            set name [lindex [split $key -] 1]
104            set events($name) 1
105        }
106        return [array names events]
107    } elseif {[llength $args] == 1} {
108        set name [lindex $args 0]
109        if {[info exists _dispatch($caller-$name)]} {
110            return $_dispatch($caller-$name)
111        }
112        return ""
113    }
114
115    # set a callback for one or more events
116    set now 0
117    set events ""
118    set callback [lindex $args end]
119    foreach str [lrange $args 0 end-1] {
120        if {$str == "-now"} {
121            set now 1
122        } elseif {[info exists _event2clients($str)]} {
123            lappend events $str
124        } else {
125            if {[string index $str 0] == "-"} {
126                error "bad option \"$str\": should be -now"
127            } else {
128                error "bad event \"$str\": should be [join [lsort [array names _event2clients]] {, }]"
129            }
130        }
131    }
132
133    #
134    # If the callback is "", then remove the callback for this
135    # caller.  Otherwise, set the callback for this caller.
136    #
137    foreach name $events {
138        cancel $name
139
140        if {"" == $callback} {
141            catch {unset _dispatch($caller-$name)}
142            if {"" == [array names _dispatch $caller-*]} {
143                set i [lsearch $_event2clients($name) $caller]
144                if {$i >= 0} {
145                    set _event2clients($name) [lreplace $_event2clients($name) $i $i]
146                }
147            }
148        } else {
149            set _dispatch($caller-$name) $callback
150            set i [lsearch $_event2clients($name) $caller]
151            if {$i < 0} { lappend _event2clients($name) $caller }
152
153            if {$now} {
154                _send $name $caller
155            }
156        }
157    }
158}
159
160# ----------------------------------------------------------------------
161# USAGE: event ?-now? ?-later? ?-idle? ?-after time? !event ?args...?
162#
163# Clients use this to dispatch an event to any callers who have
164# registered their interest.
165# ----------------------------------------------------------------------
166itcl::body Rappture::DispatchObj::event {args} {
167    set when "-now"
168    set first [lindex $args 0]
169    if {[string index $first 0] == "-"} {
170        switch -- $first {
171            -now   {
172                set when "-now"
173                set args [lrange $args 1 end]
174            }
175            -later {
176                set when 1
177                set args [lrange $args 1 end]
178            }
179            -idle  {
180                set when "-idle"
181                set args [lrange $args 1 end]
182            }
183            -after {
184                set when [lindex $args 1]
185                if {![string is int $when]} {
186                    error "bad value \"$when\": should be int (time in ms)"
187                }
188                set args [lrange $args 2 end]
189            }
190            default {
191                error "bad option \"$first\": should be -now, -later, -idle, or -after"
192            }
193        }
194    }
195
196    if {[llength $args] < 1} {
197        error "wrong # args: should be \"event ?switches? !event ?args...?\""
198    }
199    set event [lindex $args 0]
200    set args [lrange $args 1 end]
201    if {![info exists _event2clients($event)]} {
202        error "bad event \"$event\": should be [join [lsort [array names _event2clients]] {, }]"
203    }
204
205    switch -- $when {
206        -now {
207            _send $event all $args
208        }
209        -idle {
210            set _extraargs($event) $args
211            after cancel [itcl::code $this _send $event all @extra]
212            after idle [itcl::code $this _send $event all @extra]
213        }
214        default {
215            set _extraargs($event) $args
216            after cancel [itcl::code $this _send $event all @extra]
217            after $when [itcl::code $this _send $event all @extra]
218        }
219    }
220}
221
222# ----------------------------------------------------------------------
223# USAGE: cancel ?!event ...?
224#
225# Used to cancel any event notifications pending for the specified
226# list of events.  Notifications may be pending if a particular
227# event was raised with the -idle or -after flags.
228# ----------------------------------------------------------------------
229itcl::body Rappture::DispatchObj::cancel {args} {
230    foreach event $args {
231        if {![info exists _event2clients($event)]} {
232            error "bad event \"$str\": should be [join [lsort [array names _event2clients]] {, }]"
233        }
234        after cancel [itcl::code $this _send $event all @extra]
235    }
236}
237
238# ----------------------------------------------------------------------
239# USAGE: ispending !event
240#
241# Returns 1 if the specified !event is pending, and 0 otherwise.
242# Notifications may be pending if a particular event was raised
243# with the -idle or -after flags.
244# ----------------------------------------------------------------------
245itcl::body Rappture::DispatchObj::ispending {event} {
246    set cmd [itcl::code $this _send $event all @extra]
247    foreach id [after info] {
248        set cmd2 [lindex [after info $id] 0]
249        if {[string equal $cmd $cmd2]} {
250            return 1
251        }
252    }
253    return 0
254}
255
256# ----------------------------------------------------------------------
257# USAGE: _send !event caller ?arglist?
258#
259# Used internally to send an event to one or all callers.  The event
260# is sent by first invoking a callback to generate any data associated
261# with the event.  This data, along with any extra args passed in,
262# are added to the callback for the caller.
263# ----------------------------------------------------------------------
264itcl::body Rappture::DispatchObj::_send {event caller {arglist ""}} {
265    if {$caller == "all"} {
266        set caller $_event2clients($event)
267    }
268    if {$arglist == "@extra" && [info exists _extraargs($event)]} {
269        set arglist $_extraargs($event)
270    }
271
272    # if there are any clients for this event, get the arguments ready
273    set any 0
274    foreach cname $caller {
275        if {[info exists _dispatch($cname-$event)]} {
276            set any 1
277            break
278        }
279    }
280
281    set dargs ""
282    if {$any && [string length $_event2datacb($event)] > 0} {
283        # get the args from the data callback
284        set status [catch {
285            uplevel #0 $_event2datacb($event) $event
286        } dargs]
287
288        if {$status != 0} {
289            # anything go wrong? then throw a background error
290            bgerror "$dargs\n(while dispatching $event)"
291            set dargs ""
292        }
293    }
294    # add any arguments added from command line
295    eval lappend dargs $arglist
296
297    foreach cname $caller {
298        if {[info exists _dispatch($cname-$event)]} {
299            set status [catch {
300                uplevel #0 $_dispatch($cname-$event) event $event $dargs
301            } result]
302
303            if {$status != 0} {
304                # anything go wrong? then throw a background error
305                bgerror "$result\n(while dispatching $event to $cname)"
306            }
307        }
308    }
309}
310
311# ----------------------------------------------------------------------
312# BASE CLASS: Dispatcher
313# ----------------------------------------------------------------------
314itcl::class Rappture::Dispatcher {
315    constructor {args} {
316        Rappture::dispatcher _dispatcher
317        $_dispatcher register !destroy
318
319        eval configure $args
320    }
321
322    destructor {
323        event !destroy
324    }
325
326    public method dispatch {args} {
327        eval $_dispatcher dispatch $args
328    }
329
330    protected method register {args} {
331        eval $_dispatcher register $args
332    }
333    protected method event {args} {
334        eval $_dispatcher event $args object $this
335    }
336
337    private variable _dispatcher ""  ;# dispatcher for events
338}
Note: See TracBrowser for help on using the repository browser.