Discussion:
wxsqueak mailing list
Cees de Groot
2006-07-19 15:18:00 UTC
Permalink
Now that we're chatting. One issue seems to creep up when exposing the
software to actual users over and over again: when an event is being
processed, widgets are not disabled. So if you have a reasonably
lengthy operation, we've seen users getting impatient and starting to
go into 'gremlin mode' (just clicking around), which prompts a message
from wx about max event stack depth reached and a subsequent goodbye
from the image.
I'm plastering the software with WxWindowDisabler instances, but that
doesn't feel like the final solution, somehow ;)
Ah, real users can be such a pain. I haven't tried long delays like
this, so I'll have to see what's going on. At worst, we could add some
kind of WxReallyDisableItDammit primitive.
Or maybe always disable The Works while an event is being handled inside
Squeak. But I don't know whether that'll work (although it's probably easy
enough to try...)
Steven Swerling
2006-07-19 15:18:00 UTC
Permalink
Post by Cees de Groot
Now that we're chatting. One issue seems to creep up when exposing
the software to actual users over and over again: when an event is
being processed, widgets are not disabled. So if you have a
reasonably lengthy operation, we've seen users getting impatient
and starting to go into 'gremlin mode' (just clicking around),
which prompts a message from wx about max event stack depth reached
and a subsequent goodbye from the image.
I'm plastering the software with WxWindowDisabler instances, but
that doesn't feel like the final solution, somehow ;)
Ah, real users can be such a pain. I haven't tried long delays like
this, so I'll have to see what's going on. At worst, we could add
some kind of WxReallyDisableItDammit primitive.
Or maybe always disable The Works while an event is being handled
inside Squeak. But I don't know whether that'll work (although it's
probably easy enough to try...)
Judicious usage of deferred event handling would also help for this
problem. The event stack would not fill up if callbacks that take a long
time to process are handled from a WorldState>>addDeferred: type
function instead of synchonously. If you're not specifically calculating
something to be sent back to the WxWidgets libraries (a list of strings
for a menu or list box, for instance), than why clog up the event queue?

I'll officially stop harping on this point. After all, I can always just
patch it in for my own apps w/out effecting anything else. Just wanted
to get "on record". If callbacks that take a long time get passed off to
another process for handling, the "event stack" problem goes away.
Rob Gayvert
2006-07-19 15:18:00 UTC
Permalink
Post by Steven Swerling
On Tue, 22 Feb 2005 07:56:51 -0500, Rob Gayvert
Now that we're chatting. One issue seems to creep up when exposing
the software to actual users over and over again: when an event
is being processed, widgets are not disabled. So if you have a
reasonably lengthy operation, we've seen users getting impatient
and starting to go into 'gremlin mode' (just clicking around),
which prompts a message from wx about max event stack depth
reached and a subsequent goodbye from the image.
I'm plastering the software with WxWindowDisabler instances, but
that doesn't feel like the final solution, somehow ;)
Ah, real users can be such a pain. I haven't tried long delays like
this, so I'll have to see what's going on. At worst, we could add
some kind of WxReallyDisableItDammit primitive.
Or maybe always disable The Works while an event is being handled
inside Squeak. But I don't know whether that'll work (although it's
probably easy enough to try...)
Judicious usage of deferred event handling would also help for this
problem. The event stack would not fill up if callbacks that take a
long time to process are handled from a WorldState>>addDeferred: type
function instead of synchonously. If you're not specifically
calculating something to be sent back to the WxWidgets libraries (a
list of strings for a menu or list box, for instance), than why clog
up the event queue?
I'll officially stop harping on this point. After all, I can always
just patch it in for my own apps w/out effecting anything else. Just
wanted to get "on record". If callbacks that take a long time get
passed off to another process for handling, the "event stack" problem
goes away.
Using deferred events should definitely help prevent crashes, and as a
general policy should be used for anything that takes any appreciable
amount of time. But I'm not sure what will happen in this scenario if
events aren't disabled or consumed in some way. If they pile up in the
wx queue while the deferred event is being handled, there's no easy way
to ignore them. Should each deferred event be handled in its own
process? That would prevent wx events from queing up, but would it make
it harder to deal with at the application level?

Don't stop harping on this; you're absolutely right to be concerned.
I've been doing simple demos that don't have to deal with real-world
situations like this, so I haven't been worrying about these kinds of
problems. This is something we have to handle properly in the core, or
we'll wind up with a lot of ugly application level code.
Cees de Groot
2006-07-19 15:18:00 UTC
Permalink
Post by Rob Gayvert
Using deferred events should definitely help prevent crashes, and as a
general policy should be used for anything that takes any appreciable
amount of time. But I'm not sure what will happen in this scenario if
events aren't disabled or consumed in some way. If they pile up in the
wx queue while the deferred event is being handled, there's no easy way
to ignore them. Should each deferred event be handled in its own
process? That would prevent wx events from queing up, but would it make
it harder to deal with at the application level?
Of course, the application then has to deal with lots of events queued up,
each consuming appreciable time. For example, say you have retrieved a
dataset of 10,000 items and there's a refresh button. If the user keeps
hammering it and you just work around the issue by deferring the extra
hits on the button, your user is going to stare a long time at the dataset
being refreshed over and over again...

That is solvable on an application level, of course, but not much cleaner
than what I currently use (WxWindowDisabler for everything that may take
long). It's probably cleaner to catch this on a lower level.
Steven Swerling
2006-07-19 15:18:00 UTC
Permalink
Post by Rob Gayvert
Don't stop harping on this;
Ok, since you asked :)
Post by Rob Gayvert
Post by Rob Gayvert
Using deferred events should definitely help prevent crashes, and as a
general policy should be used for anything that takes any appreciable
amount of time. But I'm not sure what will happen in this scenario if
events aren't disabled or consumed in some way. If they pile up in the
wx queue while the deferred event is being handled, there's no easy way
to ignore them. Should each deferred event be handled in its own
process? That would prevent wx events from queing up, but would it make
it harder to deal with at the application level?
No, I would just use a SharedQueue to store the deferred events as they
come in, and have another process whirring away that plucks the deferred
events one by one and executes them. That's how WorldState's deferred
queue works, and I believe that's how Digitalk/v handled it. Having one
process per event will likely get you some synchronization problems.

At this point you've shifted the burden to the app developer. There is
always the possibility that a deferred event and a sync event get in a
tug of war over some data. But that's the way Digitalk/V dealt with it
-- you're already way ahead of them, since in Digi you would crash the
image if there was a walkback during a callback. I remember their advice
to developers overriding windows callbacks was quite simple -- get out
fast, or pay the consequences. And it worked. In practice, it really
wasn't hard to deal with. It worked.

Picture this scenario. Say a developer hooks up a button click event to
load and start an mpeg movie. The load and startup take awhile, so it is
hooked up as a "deferred" event. The handleEvent: method sees that it's
a deferred event and queues it up, returning from the method. The
developer doesn't use an hourglass cursor, and the user doesn't know
whats going on, so he clicks the button about 50 times. Boom, 50
"startVideo" commands are queued up.

But the app developer was smart enough to put a check at the top of his
#startVideo method that checks to see if the video is already loaded,
and exit the method if it's already going, just outputting a status
message blinking "Currently loading my.mpeg" for the subsequent 49
events (which therefore execute almost instantly).

I don't think there is a silver bullet that can make callbacks go away
as a problem. But deferred events would help make it a very tractable
problem.

The event
Rob Gayvert
2006-07-19 15:18:00 UTC
Permalink
Post by Steven Swerling
Post by Rob Gayvert
Don't stop harping on this;
Ok, since you asked :)
Post by Rob Gayvert
Post by Rob Gayvert
Using deferred events should definitely help prevent crashes, and as
a general policy should be used for anything that takes any
appreciable amount of time. But I'm not sure what will happen in
this scenario if events aren't disabled or consumed in some way. If
they pile up in the wx queue while the deferred event is being
handled, there's no easy way to ignore them. Should each deferred
event be handled in its own process? That would prevent wx events
from queing up, but would it make it harder to deal with at the
application level?
No, I would just use a SharedQueue to store the deferred events as
they come in, and have another process whirring away that plucks the
deferred events one by one and executes them. That's how WorldState's
deferred queue works, and I believe that's how Digitalk/v handled it.
Having one process per event will likely get you some synchronization
problems.
Ah, that answers the remaining question I had about how to implement
this. I could see 3 ways of processing the deferred events: (a)
synchronously before other wx events in the current event process; (b)
in a single separate process; or (c) each in a separate process. The
first looks too strict, and the third too lenient, so the second one
must be right.
Post by Steven Swerling
At this point you've shifted the burden to the app developer. There is
always the possibility that a deferred event and a sync event get in a
tug of war over some data. But that's the way Digitalk/V dealt with it
-- you're already way ahead of them, since in Digi you would crash the
image if there was a walkback during a callback. I remember their
advice to developers overriding windows callbacks was quite simple --
get out fast, or pay the consequences. And it worked. In practice, it
really wasn't hard to deal with. It worked.
Picture this scenario. Say a developer hooks up a button click event
to load and start an mpeg movie. The load and startup take awhile, so
it is hooked up as a "deferred" event. The handleEvent: method sees
that it's a deferred event and queues it up, returning from the
method. The developer doesn't use an hourglass cursor, and the user
doesn't know whats going on, so he clicks the button about 50 times.
Boom, 50 "startVideo" commands are queued up.
But the app developer was smart enough to put a check at the top of
his #startVideo method that checks to see if the video is already
loaded, and exit the method if it's already going, just outputting a
status message blinking "Currently loading my.mpeg" for the subsequent
49 events (which therefore execute almost instantly).
Or you could also just disable the button, no? The scenario that Cees
describes with a lengthy database query is very similar. If the handler
is executing Smalltalk code, then subsequent clicks elsewhere could
likewise be handled immediately and either ignored or with some kind of
busy response. But what if it's off in a primitive or FFI call for an
extended period? I don't know what the wx core will do with clicks or
keystrokes in this case. Maybe there's something we can use here from
the implementation of WxWindowDisabler.
Steven Swerling
2006-07-19 15:18:00 UTC
Permalink
Post by Rob Gayvert
Or you could also just disable the button, no? The scenario that Cees
describes with a lengthy database query is very similar. If the handler
is executing Smalltalk code, then subsequent clicks elsewhere could
likewise be handled immediately and either ignored or with some kind of
busy response. But what if it's off in a primitive or FFI call for an
extended period? I don't know what the wx core will do with clicks or
keystrokes in this case. Maybe there's something we can use here from
the implementation of WxWindowDisabler.
Yes, agreed on all counts. I lost a little bit the courage of my
convictions as soon as I hit the send button on that last deferred
events email. Cees was right that it doesn't clean up the problem, it
just shifts it.

Perhaps, as you proceed with this difficult problem, you might want to
leave hooks in the vm so that you can experiment *from smalltalk* with
different approaches. For example, if you implement a loop in the VM
that "eats" events during a lengthy sync callback, perhaps you could
leave a hook in Smalltalk that turns the filter on and off. That way the
masses can experiment with approaches to the problem by controlling
event processing from the image. The guy that writes a better WinAmp
decides not to eat any events, and just handle the consequences by
putting checks into his own code. The guy that writes a banking
application says "no, too risky" and disables the interface during
transaction download.
Rob Gayvert
2006-07-19 15:18:00 UTC
Permalink
Post by Steven Swerling
Post by Rob Gayvert
Or you could also just disable the button, no? The scenario that
Cees describes with a lengthy database query is very similar. If the
handler is executing Smalltalk code, then subsequent clicks elsewhere
could likewise be handled immediately and either ignored or with some
kind of busy response. But what if it's off in a primitive or FFI
call for an extended period? I don't know what the wx core will do
with clicks or keystrokes in this case. Maybe there's something we
can use here from the implementation of WxWindowDisabler.
Yes, agreed on all counts. I lost a little bit the courage of my
convictions as soon as I hit the send button on that last deferred
events email. Cees was right that it doesn't clean up the problem, it
just shifts it.
Perhaps, as you proceed with this difficult problem, you might want to
leave hooks in the vm so that you can experiment *from smalltalk* with
different approaches. For example, if you implement a loop in the VM
that "eats" events during a lengthy sync callback, perhaps you could
leave a hook in Smalltalk that turns the filter on and off. That way
the masses can experiment with approaches to the problem by
controlling event processing from the image. The guy that writes a
better WinAmp decides not to eat any events, and just handle the
consequences by putting checks into his own code. The guy that writes
a banking application says "no, too risky" and disables the interface
during transaction download.
Here's a first pass at adding deferred event handling. There's a
'Deferred Events' demo in 'Other Samples' that exercises several
variations on this theme. WxWindowDisabler seems to do a good job of
blocking all events.
-------------- next part --------------
'From Squeak3.7 of ''4 September 2004'' [latest update: #5989] on 23 February 2005 at 12:36:18 pm'!
WxObject subclass: #WxEvtHandler
instanceVariableNames: 'eventHandlers'
classVariableNames: 'CallbackProcesses CompositeEvents CurrentEvent DeferredEventProcess DeferredEvents EventClasses EventProcess EventSymbols ShowEvents Synonyms'
poolDictionaries: ''
category: 'WxWidgets-Events'!
WxPanel subclass: #WxDeferredButtonDemo
instanceVariableNames: 'button1 button2 button3 button4 button5 button6 button7'
classVariableNames: ''
poolDictionaries: ''
category: 'WxWidgets-Demo'!

!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 10:15'!
addDeferredEvent: anArray

self class deferredEvents nextPut: anArray.! !

!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:28'!
handleEvent: aWxEvent

"Evaluate some Smalltalk code to process the given event. We get here from a synchronous event
callback from wx."

| arr eventSymbol handler |

eventSymbol := self class valueToSymbol: aWxEvent getEventType.

(self eventHandlers includesKey: eventSymbol) ifFalse: [
"try a synonym"
eventSymbol := self class synonyms keyAtValue: eventSymbol ifAbsent: [ eventSymbol ].
].

handler := self eventHandlers at: eventSymbol ifAbsent: [
Transcript show: self; show: ': no handlers for '; show: eventSymbol; cr.
^false
].

"Each handler should be an array containing an object, an action, and optionally a boolean deferred value.
The action may be a selector, a block, or an array. A block action is evaluated with the event; the object is
ignored. An array action allows an argument to be passed in along with a selector. If the handler is deferred,
it is placed in the DeferredEvents queue for evaluation after "

arr := handler at: aWxEvent getId ifAbsent: [
handler at: -1 ifAbsent: [
Transcript show: 'WxEvtHandler.handleEvent: no handler for ', aWxEvent asString; cr.
^false
]
].

((arr size > 2) and: [ arr third ]) ifTrue: [
self addDeferredEvent: (Array with: aWxEvent clone with: arr first with: arr second).
^true
].

self class processAction: (Array with: aWxEvent with: arr first with: arr second).

^true! !

!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:22'!
on: eventSymbol id: id send: selector

self on: eventSymbol id: id send: selector to: self.
! !

!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:21'!
on: eventSymbol id: id send: selector to: anObject

self on: eventSymbol id: id send: selector to: anObject deferred: false.
! !

!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:20'!
on: eventSymbol id: id send: selector to: anObject deferred: aBoolean

| eventType |

"Make sure eventSymbol is a unary selector -- it's easy to goof and tough to find"
eventSymbol isUnary
ifFalse: [ self error: 'event symbol #', eventSymbol, ' should be unary' ].

(CompositeEvents includesKey: eventSymbol) ifTrue: [
^self onComposite: eventSymbol id: id send: selector to: anObject
].

eventType := self class symbolToValue: eventSymbol.

(eventType == 0) ifTrue: [
self error: 'unknown event symbol #', eventSymbol.
].

(self handlerFor: eventSymbol) at: id put: (Array with: anObject with: selector with: aBoolean).

Wx connect: self
id: id
type: (self class symbolToValue: eventSymbol).

! !

!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:22'!
on: eventSymbol id: id sendDeferred: selector

^self on: eventSymbol id: id send: selector to: self deferred: false
! !

!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:22'!
on: eventSymbol id: id sendDeferred: selector to: anObject

self on: eventSymbol id: id send: selector to: anObject deferred: true.
! !

!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:23'!
on: eventSymbol sendDeferred: selector

self on: eventSymbol sendDeferred: selector to: self.! !

!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:23'!
on: eventSymbol sendDeferred: selector to: anObject

self on: eventSymbol id: wxIdAny send: selector to: anObject deferred: true.! !

!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:20'!
onComposite: eventSymbol id: id send: selector to: anObject

self onComposite: eventSymbol id: id send: selector to: anObject deferred: false.! !

!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:20'!
onComposite: eventSymbol id: id send: selector to: anObject deferred: aBoolean

( CompositeEvents at: eventSymbol ifAbsent: [ ^nil ]) do: [:evt |
self on: evt id: id send: selector to: anObject deferred: aBoolean
].! !


!WxDeferredButtonDemo methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 09:58'!
build

self setBackgroundColour: wxWhite.

button1 := WxButton parent: self id: wxIdAny label: 'Regular send with delays' position: ***@20.
button1 on: #wxEvtCommandButtonClicked send: #onClick1: to: self.
button1 setToolTipString: 'Push me and wait...'.

button2 := WxButton parent: self id: wxIdAny label: 'Defer with delays' position: ***@60.
button2 on: #wxEvtCommandButtonClicked sendDeferred: #onClick2: to: self.
button2 setToolTipString: 'Push me and wait...'.

button3 := WxButton parent: self id: wxIdAny label: 'Defer with wxSleep' position: ***@100.
button3 on: #wxEvtCommandButtonClicked sendDeferred: #onClick3: to: self.
button3 setToolTipString: 'Push me and wait...'.

button4 := WxButton parent: self id: wxIdAny label: 'Regular send without disabling - keep clicking to hit limit' position: ***@140.
button4 on: #wxEvtCommandButtonClicked send: #onClick4: to: self.
button4 setToolTipString: 'Push me and wait...'.

button5 := WxButton parent: self id: wxIdAny label: 'Regular send with WxWindowDisabler' position: ***@180.
button5 on: #wxEvtCommandButtonClicked send: #onClick5: to: self.
button5 setToolTipString: 'Push me and wait...'.

button6 := WxButton parent: self id: wxIdAny label: 'Regular send with long primitive' position: ***@220.
button6 on: #wxEvtCommandButtonClicked send: #onClick6: to: self.
button6 setToolTipString: 'Push me and wait...'.

button7 := WxButton parent: self id: wxIdAny label: 'Defer with long primitive' position: ***@260.
button7 on: #wxEvtCommandButtonClicked sendDeferred: #onClick7: to: self.
button7 setToolTipString: 'Push me and wait...'.


! !

!WxDeferredButtonDemo methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 08:15'!
onClick1: aCommandEvent

Wx logMessage: 'Button 1 click'.

button1 disable.

1 to: 5 do: [:i |
Wx logMessage: 'Button 1 step ', i asString.
(Delay forSeconds: 1) wait.
].

button1 enable.! !

!WxDeferredButtonDemo methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 08:10'!
onClick2: aCommandEvent

Wx logMessage: 'Button 2 click'.

button2 disable.

1 to: 5 do: [:i |
Wx logMessage: 'Button 2 step ', i asString.
(Delay forSeconds: 1) wait.
].

button2 enable.! !

!WxDeferredButtonDemo methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 08:19'!
onClick3: aCommandEvent

Wx logMessage: 'Button 3 click'.

button3 disable.

1 to: 5 do: [:i |
Wx logMessage: 'Button 3 step ', i asString.
Wx sleep: 1.
].

button3 enable.! !

!WxDeferredButtonDemo methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 08:25'!
onClick4: aCommandEvent

Wx logMessage: 'Button 4 click'.

1 to: 5 do: [:i |
Wx logMessage: 'Button 4 step ', i asString.
(Delay forSeconds: 1) wait.
].

! !

!WxDeferredButtonDemo methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 08:41'!
onClick5: aCommandEvent

Wx logMessage: 'Button 5 click'.

WxWindowDisabler disableEventsWhile: [
1 to: 5 do: [:i |
Wx logMessage: 'Button 5 step ', i asString.
(Delay forSeconds: 1) wait.
].
].

Wx logMessage: 'Button 5 done'.

! !

!WxDeferredButtonDemo methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 10:03'!
onClick6: aCommandEvent

| x |

Wx logMessage: 'Button 6 click'.

button6 disable.

x := FloatArray new: 2000000.
x += 1.
Wx logMessage: 'Starting long primitive'.
x squaredLength.
Wx logMessage: 'Done with long primitive'.

button6 enable.

Wx logMessage: 'Button 6 done'.
! !

!WxDeferredButtonDemo methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 10:02'!
onClick7: aCommandEvent

| x |

Wx logMessage: 'Button 7 click'.

button7 disable.

x := FloatArray new: 2000000.
x += 1.
Wx logMessage: 'Starting long primitive'.
x squaredLength.
Wx logMessage: 'Done with long primitive'.

button7 enable.

Wx logMessage: 'Button 7 done'.

! !


!WxDemoFrame methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 22:06'!
demoItems

^#(
( 'Frames and Dialogs' (
('Dialog' 'WxSampleDialog' ('WxDialog'))
('Frame' 'WxFrameDemo' ('WxFrame'))
"('MDIWindows' 'WxMDIWindowsDemo')"
('MiniFrame' 'WxMiniFrameDemo' ('WxMiniFrame'))
('Wizard' 'WxWizardSample' ('WxWizardDemoTitledPage' 'WxWizardDemoSkipNextPage' 'WxWizard' 'WxWizardPageSimple' ))
))
( 'Common Dialogs' (
('ColourDialog' 'WxColourDialogDemo' ('WxColourDialog') )
('DirDialog' 'WxDirDialogDemo' ('WxDirDialog') )
('FileDialog' 'WxFileDialogDemo' ('WxFileDialog') )
('FindReplaceDialog' 'WxFindReplaceDialogDemo' ('WxFindReplaceDialog') )
('FontDialog' 'WxFontDialogDemo' ('WxFontDialog') )
('MessageDialog' 'WxMessageDialogDemo' ('WxMessageDialog') )
('PageSetupDialog' 'WxPageSetupDialogDemo' ('WxPageSetupDialog') )
('PrintDialog' 'WxPrintDialogDemo' ('WxPrintDialog') )
('ProgressDialog' 'WxProgressDialogDemo' ('WxProgressDialog') )
('SingleChoiceDialog' 'WxSingleChoiceDialogDemo' ('WxSingleChoiceDialog') )
('TextEntryDialog' 'WxTextEntryDialogDemo' ('WxTextEntryDialog') )
))
"
( 'More Dialogs' (
('ImageBrowser' 'WxImageBrowserDemo')
('MultipleChoiceDialog' 'WxMultipleChoiceDialogDemo')
('ScrolledMessageDialog' 'WxScrolledMessageDialogDemo')
))
"
( 'Core Windows/Controls' (
('BitmapButton' 'WxBitmapButtonDemo' ('WxBitmapButton') )
('Button' 'WxButtonDemo' ('WxButton') )
('CheckBox' 'WxCheckBoxDemo' ('WxCheckBox') )
('CheckListBox' 'WxCheckListBoxDemo' ('WxCheckListBox') )
('Choice' 'WxChoiceDemo' ('WxChoice') )
('ComboBox' 'WxComboBoxDemo' ('WxComboBox') )
('Gauge' 'WxGaugeDemo' ('WxGauge') )
('Grid' 'WxGridDemo' ('WxSimpleGrid' 'WxStdEdRendGrid' 'WxCustomDataTableGrid' 'WxHugeTableGrid' 'WxEnterHandlerGrid' 'WxCustEditorGrid' 'WxDragableGrid' 'WxDragAndDropGrid' ))
('GridMegaExample' 'WxGridMegaDemo' ('WxMegaFontRenderer' 'WxMegaGrid' 'WxMegaImageRenderer' 'WxMegaTable'))
('ListBox' 'WxListBoxDemo' ('WxListBox') )
('ListCtrl' 'WxListCtrlDemo' ('WxListCtrl') )
('ListCtrlVirtual' 'WxVirtualListCtrlDemo' ('WxVirtualListCtrl') )
('Listbook' 'WxListbookDemo' ('WxListbook') )
('Menu' 'WxMenuDemo' ('WxMenu') )
('Notebook' 'WxNotebookDemo' ('WxDemoColorPanel' 'WxScrolledWindowDemo' 'WxSimpleGrid' 'WxListCtrlDemo'))
"
('PopupMenu' 'WxPopupMenuDemo' ('WxMenu') )
('PopupWindow' 'WxPopupWindowDemo' ('WxPopupWindow' 'WxPopupTransientWindow') )
"
('RadioBox' 'WxRadioBoxDemo' ('WxRadioBox') )
('RadioButton' 'WxRadioButtonDemo' ('WxRadioButton') )
('SashWindow' 'WxSashWindowDemo' ('WxSashWindow') )
('ScrolledWindow' 'WxScrolledWindowDemo' ('WxScrolledWindow') )
('Slider' 'WxSliderDemo' ('WxSlider') )
('SpinButton' 'WxSpinButtonDemo' ('WxSpinButton') )
('SpinCtrl' 'WxSpinCtrlDemo' ('WxSpinCtrl') )
('SplitterWindow' 'WxSplitterWindowDemo' ('WxSplitterWindow') )
('StaticBitmap' 'WxStaticBitmapDemo' ('WxStaticBitmap') )
('StaticText' 'WxStaticTextDemo' ('WxStaticText') )
('StatusBar' 'WxStatusBarDemo' ('WxStatusBar') )
('TextCtrl' 'WxTextCtrlDemo' ('WxTextCtrl') )
('ToggleButton' 'WxToggleButtonDemo' ('WxToggleButton') )
('ToolBar' 'WxToolBarDemo' ('WxToolBar') )
('TreeCtrl' 'WxTreeCtrlDemo' ('WxTreeCtrl') )
('Validator' 'WxValidatorDemo' ('WxDemoValidator' 'WxDemoValidatorDialog' 'WxDemoTextValidator'))
))
"
( 'Custom Controls' (
('AnalogClockWindow' 'WxAnalogClockWindowDemo')
('ColourSelect' 'WxColourSelectDemo')
('Editor' 'WxEditorDemo')
('GenericButtons' 'WxGenericButtonsDemo')
('GenericDirCtrl' 'WxGenericDirCtrlDemo')
('LEDNumberCtrl' 'WxLEDNumberCtrlDemo')
('MultiSash' 'WxMultiSashDemo')
('PopupControl' 'WxPopupControlDemo')
('ColourChooser' 'WxColourChooserDemo')
('TreeListCtrl' 'WxTreeListCtrlDemo')
))
"
( 'More Windows/Controls' (
('CalendarCtrl' 'WxCalendarCtrlDemo' ('WxCalendarCtrl') )
('ContextHelp' 'WxContextHelpDemo' ('WxContextHelp') )
('MimeTypesManager' 'WxMimeTypesManagerDemo' ('WxMimeTypesManager') )
('StyledTextCtrl1' 'WxStyledTextCtrlDemo1' ('WxStyledTextCtrl') )
('VListBox' 'WxVListBoxDemo' ('WxVListBox') )

" ('ActiveXFlashWindow' 'ActiveXFlashWindowDemo')
('ActiveXIEHtmlWindow' 'ActiveXIEHtmlWindowDemo')
('ActiveXPDFWindow' 'ActiveXPDFWindowDemo')
('Calendar' 'CalendarDemo')
('DynamicSashWindow' 'DynamicSashWindowDemo')
('EditableListBox' 'EditableListBoxDemo')
('FancyText' 'FancyTextDemo')
('FileBrowseButton' 'FileBrowseButtonDemo')
('FloatBar' 'FloatBarDemo')
('FloatCanvas' 'FloatCanvasDemo')
('HtmlWindow' 'HtmlWindowDemo')
('IntCtrl' 'IntCtrlDemo')
('MVCTree' 'MVCTreeDemo')
('MaskedEditControls' 'MaskedEditControlsDemo')
('MaskedNumCtrl' 'MaskedNumCtrlDemo')
('ScrolledPanel' 'ScrolledPanelDemo')
('SplitTree' 'SplitTreeDemo')
('StyledTextCtrl2' 'WxStyledTextCtrlDemo2')
('TablePrint' 'TablePrintDemo')
('Throbber' 'ThrobberDemo')
('TimeCtrl' 'TimeCtrlDemo')
"
))
( 'Window Layout' (
('GridBagSizer' 'WxGridBagSizerDemo' ('WxGridBagSizer') )
('LayoutConstraints' 'WxLayoutConstraintsDemo' ('WxLayoutConstraints') )
('Sizers' 'WxSizersDemo' ('WxSizersDemoTestFrame' 'WxSizersDemoSampleWindow' 'WxSizer' 'WxBoxSizer' 'WxGridSizer' 'WxFlexGridSizer') )
('XmlResource' 'WxXmlResourceDemo' ('WxXmlResource') )
('XmlResourceHandler' 'WxXmlResourceHandlerDemo' ('WxXmlResourceCustomHandler' 'WxXmlCustomPanel') )
('XmlResourceSubclass' 'WxXmlResourceSubclassDemo' ('WxXmlCustomPanel2') )
"('LayoutAnchors' 'WxLayoutAnchorsDemo')
('Layoutf' 'WxLayoutfDemo')
('RowColSizer' 'WxRowColSizerDemo')
('ScrolledPanel' 'WxScrolledPanelDemo')"
))
"
( 'Processes and Events' (
('EventManager' 'WxEventManagerDemo')
('KeyEvents' 'WxKeyEventsDemo')
('Process' 'WxProcessDemo')
('CustomEvents' 'WxCustomEventsDemo')
('Threads' 'WxThreadsDemo')
('Timer' 'WxTimerDemo')
))
( 'Clipboard and DnD' (
('CustomDragAndDrop' 'WxCustomDragAndDropDemo')
('DragAndDrop' 'WxDragAndDropDemo')
('URLDragAndDrop' 'WxURLDragAndDropDemo')
))
"
( 'Using Images' (
('ArtProvider' 'WxArtProviderDemo' ('WxArtProvider') )
('Cursor' 'WxCursorDemo' ('WxCursor') )
('DragImage' 'WxDragImageSampleCanvas' ('WxDragImageSample' 'WxDragShape') )
('Image' 'WxImageDemo' ('WxImage') )
('ImageAlpha' 'WxImageAlphaDemo' ('WxImage') )
('Mask' 'WxMaskDemo' ('WxImage') )
"('ImageFromStream' 'WxImageFromStreamDemo')
('Throbber' 'WxThrobberDemo')"
))
( 'Miscellaneous' (
('DrawXXXList' 'WxDrawXXXListDemo' ('WxDC') )
('FileHistory' 'WxFileHistoryDemo' ('WxFileHistory') )
('FontEnumerator' 'WxFontEnumeratorDemo' ('WxFontEnumerator') )
('PrintFramework' 'WxPrintFrameworkDemo' ('WxDemoPrintout') )
('ShapedWindow' 'WxShapedWindowDemo' ('WxFrame') )
"('ColourDB' 'WxColourDBDemo')
('Joystick' 'WxJoystickDemo')
('OGL' 'WxOGLDemo')
('Sound' 'WxSoundDemo')
('Unicode' 'WxUnicodeDemo')"
))
( 'Other Samples' (
('Minimal' 'WxMinimalSample')
('AboutBox' 'WxHtmlAboutSample' ('WxHtmlWindow') )
('Deferred Events' 'WxDeferredButtonDemo' )
('Dialogs' 'WxDialogSample' ('WxDialogSampleCanvas') )
('Controls' 'WxControlsSample' ('WxControl') )
('HelpController' 'WxHelpControllerDemo' ('WxHelpController') )
"('DragImage2' 'WxDragImageSample' ('WxDragImageSampleCanvas' 'WxDragShape') )
('StyledText' 'WxSTCSample' ('WxStyledTextCtrl') )
('SplitterWindow2' 'WxSplitterSample' ('WxSplitterTestCanvas') )"
('SqueakDevTools' 'WxDevToolsSample' ('BrowserPresenter') )
('XRC Sample' 'WxXrcDemoFrame' ('WxXrcDemoPreferencesDialog' 'WxXrcDemoResizableListCtrl') )
('XRC Viewer' 'WxXrcViewerDemo' ('XMLDOMParser') )
))
)! !


!WxEvtHandler class methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 10:15'!
deferredEvents

^ DeferredEvents ifNil: [ DeferredEvents := SharedQueue new ]! !

!WxEvtHandler class methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 10:16'!
initialize

DeferredEvents := nil.

self initializeSynonyms.
self initializeCompositeEvents.
self initializeEventSymbols.
self initializeEventClasses.

! !

!WxEvtHandler class methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:28'!
processAction: anArray

| obj action evt |

evt := anArray first.
obj := anArray second.
action := anArray third.

action isBlock ifTrue: [
"special case of a block context"
action value: evt.
^true
].

(action class == Array) ifTrue: [
"special case of a selector-arg pair (see WxMenu>>add:target:selector:argument)"
obj perform: action first with: action second.
^true
].

action isUnary ifTrue: [
obj perform: action.
]
ifFalse: [
obj perform: action with: evt.
].! !

!WxEvtHandler class methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 10:16'!
runDeferredEventProcess

DeferredEventProcess ifNotNil: [ self stopDeferredEventProcess ].

DeferredEventProcess :=
[
[ true ] whileTrue: [
self processAction: self deferredEvents next.
].
] forkAt: Processor userSchedulingPriority.


! !

!WxEvtHandler class methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 08:27'!
runEventProcess

EventProcess ifNotNil: [ self stopEventProcess ].

EventProcess :=
[
[ true ] whileTrue: [
(Delay forMilliseconds: 5) wait.
[ Wx processEvents == true ] whileTrue: [].

].
] forkAt: Processor userSchedulingPriority.


! !

!WxEvtHandler class methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 21:47'!
startingUp: resuming

self runEventProcess.
self startCallbackProcesses.
self runDeferredEventProcess.
! !

!WxEvtHandler class methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 21:45'!
stopDeferredEventProcess

DeferredEventProcess ifNotNil: [ DeferredEventProcess terminate ].
DeferredEventProcess := nil.! !


!WxDeferredButtonDemo class methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 22:05'!
testPanel: parent

^(self parent: parent) build

! !


!WxWindowDisabler class methodsFor: 'generated' stamp: 'rtg 2/23/2005 08:39'!
disableEventsWhile: aBlock

| disabler |

disabler := self new.
[ aBlock value ] ensure: [ disabler delete ].! !

WxEvtHandler initialize!

!WxEvtHandler class reorganize!
('as yet unclassified' callbackProcesses checkCallbackException cloneCurrentEvent compositeEvents convertWxEventNameToSymbol: deferredEvents eventClassForEventType:eventObject: eventClasses eventDeleted: eventProcess eventSymbols initialize initializeCompositeEvents initializeEventClasses initializeEventSymbols initializeSynonyms processAction: processEvent runCallbackProcess: runDeferredEventProcess runEventProcess showEvents showEvents: shuttingDown: startCallbackProcesses startingUp: stopCallbackProcess: stopCallbackProcesses stopDeferredEventProcess stopEventProcess symbolToValue: synonyms valueToSymbol:)
('generated' new)
!

WxEvtHandler startingUp: true.!
Steven Swerling
2006-07-19 15:18:00 UTC
Permalink
Sorry for lack of response of this. I *will* look at this. I ought to,
after all my yacking on this. I want to work on the packaging stuff
first, though, so it'll take me a day or two before I look.

Thanks.
Post by Rob Gayvert
Post by Steven Swerling
Post by Rob Gayvert
Or you could also just disable the button, no? The scenario that
Cees describes with a lengthy database query is very similar. If the
handler is executing Smalltalk code, then subsequent clicks elsewhere
could likewise be handled immediately and either ignored or with some
kind of busy response. But what if it's off in a primitive or FFI
call for an extended period? I don't know what the wx core will do
with clicks or keystrokes in this case. Maybe there's something we
can use here from the implementation of WxWindowDisabler.
Yes, agreed on all counts. I lost a little bit the courage of my
convictions as soon as I hit the send button on that last deferred
events email. Cees was right that it doesn't clean up the problem, it
just shifts it.
Perhaps, as you proceed with this difficult problem, you might want to
leave hooks in the vm so that you can experiment *from smalltalk* with
different approaches. For example, if you implement a loop in the VM
that "eats" events during a lengthy sync callback, perhaps you could
leave a hook in Smalltalk that turns the filter on and off. That way
the masses can experiment with approaches to the problem by
controlling event processing from the image. The guy that writes a
better WinAmp decides not to eat any events, and just handle the
consequences by putting checks into his own code. The guy that writes
a banking application says "no, too risky" and disables the interface
during transaction download.
Here's a first pass at adding deferred event handling. There's a
'Deferred Events' demo in 'Other Samples' that exercises several
variations on this theme. WxWindowDisabler seems to do a good job of
blocking all events.
------------------------------------------------------------------------
'From Squeak3.7 of ''4 September 2004'' [latest update: #5989] on 23 February 2005 at 12:36:18 pm'!
WxObject subclass: #WxEvtHandler
instanceVariableNames: 'eventHandlers'
classVariableNames: 'CallbackProcesses CompositeEvents CurrentEvent DeferredEventProcess DeferredEvents EventClasses EventProcess EventSymbols ShowEvents Synonyms'
poolDictionaries: ''
category: 'WxWidgets-Events'!
WxPanel subclass: #WxDeferredButtonDemo
instanceVariableNames: 'button1 button2 button3 button4 button5 button6 button7'
classVariableNames: ''
poolDictionaries: ''
category: 'WxWidgets-Demo'!
!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 10:15'!
addDeferredEvent: anArray
self class deferredEvents nextPut: anArray.! !
!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:28'!
handleEvent: aWxEvent
"Evaluate some Smalltalk code to process the given event. We get here from a synchronous event
callback from wx."
| arr eventSymbol handler |
eventSymbol := self class valueToSymbol: aWxEvent getEventType.
(self eventHandlers includesKey: eventSymbol) ifFalse: [
"try a synonym"
eventSymbol := self class synonyms keyAtValue: eventSymbol ifAbsent: [ eventSymbol ].
].
handler := self eventHandlers at: eventSymbol ifAbsent: [
Transcript show: self; show: ': no handlers for '; show: eventSymbol; cr.
^false
].
"Each handler should be an array containing an object, an action, and optionally a boolean deferred value.
The action may be a selector, a block, or an array. A block action is evaluated with the event; the object is
ignored. An array action allows an argument to be passed in along with a selector. If the handler is deferred,
it is placed in the DeferredEvents queue for evaluation after "
arr := handler at: aWxEvent getId ifAbsent: [
handler at: -1 ifAbsent: [
Transcript show: 'WxEvtHandler.handleEvent: no handler for ', aWxEvent asString; cr.
^false
]
].
((arr size > 2) and: [ arr third ]) ifTrue: [
self addDeferredEvent: (Array with: aWxEvent clone with: arr first with: arr second).
^true
].
self class processAction: (Array with: aWxEvent with: arr first with: arr second).
^true! !
!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:22'!
on: eventSymbol id: id send: selector
self on: eventSymbol id: id send: selector to: self.
! !
!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:21'!
on: eventSymbol id: id send: selector to: anObject
self on: eventSymbol id: id send: selector to: anObject deferred: false.
! !
!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:20'!
on: eventSymbol id: id send: selector to: anObject deferred: aBoolean
| eventType |
"Make sure eventSymbol is a unary selector -- it's easy to goof and tough to find"
eventSymbol isUnary
ifFalse: [ self error: 'event symbol #', eventSymbol, ' should be unary' ].
(CompositeEvents includesKey: eventSymbol) ifTrue: [
^self onComposite: eventSymbol id: id send: selector to: anObject
].
eventType := self class symbolToValue: eventSymbol.
(eventType == 0) ifTrue: [
self error: 'unknown event symbol #', eventSymbol.
].
(self handlerFor: eventSymbol) at: id put: (Array with: anObject with: selector with: aBoolean).
Wx connect: self
id: id
type: (self class symbolToValue: eventSymbol).
! !
!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:22'!
on: eventSymbol id: id sendDeferred: selector
^self on: eventSymbol id: id send: selector to: self deferred: false
! !
!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:22'!
on: eventSymbol id: id sendDeferred: selector to: anObject
self on: eventSymbol id: id send: selector to: anObject deferred: true.
! !
!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:23'!
on: eventSymbol sendDeferred: selector
self on: eventSymbol sendDeferred: selector to: self.! !
!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:23'!
on: eventSymbol sendDeferred: selector to: anObject
self on: eventSymbol id: wxIdAny send: selector to: anObject deferred: true.! !
!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:20'!
onComposite: eventSymbol id: id send: selector to: anObject
self onComposite: eventSymbol id: id send: selector to: anObject deferred: false.! !
!WxEvtHandler methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:20'!
onComposite: eventSymbol id: id send: selector to: anObject deferred: aBoolean
( CompositeEvents at: eventSymbol ifAbsent: [ ^nil ]) do: [:evt |
self on: evt id: id send: selector to: anObject deferred: aBoolean
].! !
!WxDeferredButtonDemo methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 09:58'!
build
self setBackgroundColour: wxWhite.
button1 on: #wxEvtCommandButtonClicked send: #onClick1: to: self.
button1 setToolTipString: 'Push me and wait...'.
button2 on: #wxEvtCommandButtonClicked sendDeferred: #onClick2: to: self.
button2 setToolTipString: 'Push me and wait...'.
button3 on: #wxEvtCommandButtonClicked sendDeferred: #onClick3: to: self.
button3 setToolTipString: 'Push me and wait...'.
button4 on: #wxEvtCommandButtonClicked send: #onClick4: to: self.
button4 setToolTipString: 'Push me and wait...'.
button5 on: #wxEvtCommandButtonClicked send: #onClick5: to: self.
button5 setToolTipString: 'Push me and wait...'.
button6 on: #wxEvtCommandButtonClicked send: #onClick6: to: self.
button6 setToolTipString: 'Push me and wait...'.
button7 on: #wxEvtCommandButtonClicked sendDeferred: #onClick7: to: self.
button7 setToolTipString: 'Push me and wait...'.
! !
!WxDeferredButtonDemo methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 08:15'!
onClick1: aCommandEvent
Wx logMessage: 'Button 1 click'.
button1 disable.
1 to: 5 do: [:i |
Wx logMessage: 'Button 1 step ', i asString.
(Delay forSeconds: 1) wait.
].
button1 enable.! !
!WxDeferredButtonDemo methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 08:10'!
onClick2: aCommandEvent
Wx logMessage: 'Button 2 click'.
button2 disable.
1 to: 5 do: [:i |
Wx logMessage: 'Button 2 step ', i asString.
(Delay forSeconds: 1) wait.
].
button2 enable.! !
!WxDeferredButtonDemo methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 08:19'!
onClick3: aCommandEvent
Wx logMessage: 'Button 3 click'.
button3 disable.
1 to: 5 do: [:i |
Wx logMessage: 'Button 3 step ', i asString.
Wx sleep: 1.
].
button3 enable.! !
!WxDeferredButtonDemo methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 08:25'!
onClick4: aCommandEvent
Wx logMessage: 'Button 4 click'.
1 to: 5 do: [:i |
Wx logMessage: 'Button 4 step ', i asString.
(Delay forSeconds: 1) wait.
].
! !
!WxDeferredButtonDemo methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 08:41'!
onClick5: aCommandEvent
Wx logMessage: 'Button 5 click'.
WxWindowDisabler disableEventsWhile: [
1 to: 5 do: [:i |
Wx logMessage: 'Button 5 step ', i asString.
(Delay forSeconds: 1) wait.
].
].
Wx logMessage: 'Button 5 done'.
! !
!WxDeferredButtonDemo methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 10:03'!
onClick6: aCommandEvent
| x |
Wx logMessage: 'Button 6 click'.
button6 disable.
x := FloatArray new: 2000000.
x += 1.
Wx logMessage: 'Starting long primitive'.
x squaredLength.
Wx logMessage: 'Done with long primitive'.
button6 enable.
Wx logMessage: 'Button 6 done'.
! !
!WxDeferredButtonDemo methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 10:02'!
onClick7: aCommandEvent
| x |
Wx logMessage: 'Button 7 click'.
button7 disable.
x := FloatArray new: 2000000.
x += 1.
Wx logMessage: 'Starting long primitive'.
x squaredLength.
Wx logMessage: 'Done with long primitive'.
button7 enable.
Wx logMessage: 'Button 7 done'.
! !
!WxDemoFrame methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 22:06'!
demoItems
^#(
( 'Frames and Dialogs' (
('Dialog' 'WxSampleDialog' ('WxDialog'))
('Frame' 'WxFrameDemo' ('WxFrame'))
"('MDIWindows' 'WxMDIWindowsDemo')"
('MiniFrame' 'WxMiniFrameDemo' ('WxMiniFrame'))
('Wizard' 'WxWizardSample' ('WxWizardDemoTitledPage' 'WxWizardDemoSkipNextPage' 'WxWizard' 'WxWizardPageSimple' ))
))
( 'Common Dialogs' (
('ColourDialog' 'WxColourDialogDemo' ('WxColourDialog') )
('DirDialog' 'WxDirDialogDemo' ('WxDirDialog') )
('FileDialog' 'WxFileDialogDemo' ('WxFileDialog') )
('FindReplaceDialog' 'WxFindReplaceDialogDemo' ('WxFindReplaceDialog') )
('FontDialog' 'WxFontDialogDemo' ('WxFontDialog') )
('MessageDialog' 'WxMessageDialogDemo' ('WxMessageDialog') )
('PageSetupDialog' 'WxPageSetupDialogDemo' ('WxPageSetupDialog') )
('PrintDialog' 'WxPrintDialogDemo' ('WxPrintDialog') )
('ProgressDialog' 'WxProgressDialogDemo' ('WxProgressDialog') )
('SingleChoiceDialog' 'WxSingleChoiceDialogDemo' ('WxSingleChoiceDialog') )
('TextEntryDialog' 'WxTextEntryDialogDemo' ('WxTextEntryDialog') )
))
"
( 'More Dialogs' (
('ImageBrowser' 'WxImageBrowserDemo')
('MultipleChoiceDialog' 'WxMultipleChoiceDialogDemo')
('ScrolledMessageDialog' 'WxScrolledMessageDialogDemo')
))
"
( 'Core Windows/Controls' (
('BitmapButton' 'WxBitmapButtonDemo' ('WxBitmapButton') )
('Button' 'WxButtonDemo' ('WxButton') )
('CheckBox' 'WxCheckBoxDemo' ('WxCheckBox') )
('CheckListBox' 'WxCheckListBoxDemo' ('WxCheckListBox') )
('Choice' 'WxChoiceDemo' ('WxChoice') )
('ComboBox' 'WxComboBoxDemo' ('WxComboBox') )
('Gauge' 'WxGaugeDemo' ('WxGauge') )
('Grid' 'WxGridDemo' ('WxSimpleGrid' 'WxStdEdRendGrid' 'WxCustomDataTableGrid' 'WxHugeTableGrid' 'WxEnterHandlerGrid' 'WxCustEditorGrid' 'WxDragableGrid' 'WxDragAndDropGrid' ))
('GridMegaExample' 'WxGridMegaDemo' ('WxMegaFontRenderer' 'WxMegaGrid' 'WxMegaImageRenderer' 'WxMegaTable'))
('ListBox' 'WxListBoxDemo' ('WxListBox') )
('ListCtrl' 'WxListCtrlDemo' ('WxListCtrl') )
('ListCtrlVirtual' 'WxVirtualListCtrlDemo' ('WxVirtualListCtrl') )
('Listbook' 'WxListbookDemo' ('WxListbook') )
('Menu' 'WxMenuDemo' ('WxMenu') )
('Notebook' 'WxNotebookDemo' ('WxDemoColorPanel' 'WxScrolledWindowDemo' 'WxSimpleGrid' 'WxListCtrlDemo'))
"
('PopupMenu' 'WxPopupMenuDemo' ('WxMenu') )
('PopupWindow' 'WxPopupWindowDemo' ('WxPopupWindow' 'WxPopupTransientWindow') )
"
('RadioBox' 'WxRadioBoxDemo' ('WxRadioBox') )
('RadioButton' 'WxRadioButtonDemo' ('WxRadioButton') )
('SashWindow' 'WxSashWindowDemo' ('WxSashWindow') )
('ScrolledWindow' 'WxScrolledWindowDemo' ('WxScrolledWindow') )
('Slider' 'WxSliderDemo' ('WxSlider') )
('SpinButton' 'WxSpinButtonDemo' ('WxSpinButton') )
('SpinCtrl' 'WxSpinCtrlDemo' ('WxSpinCtrl') )
('SplitterWindow' 'WxSplitterWindowDemo' ('WxSplitterWindow') )
('StaticBitmap' 'WxStaticBitmapDemo' ('WxStaticBitmap') )
('StaticText' 'WxStaticTextDemo' ('WxStaticText') )
('StatusBar' 'WxStatusBarDemo' ('WxStatusBar') )
('TextCtrl' 'WxTextCtrlDemo' ('WxTextCtrl') )
('ToggleButton' 'WxToggleButtonDemo' ('WxToggleButton') )
('ToolBar' 'WxToolBarDemo' ('WxToolBar') )
('TreeCtrl' 'WxTreeCtrlDemo' ('WxTreeCtrl') )
('Validator' 'WxValidatorDemo' ('WxDemoValidator' 'WxDemoValidatorDialog' 'WxDemoTextValidator'))
))
"
( 'Custom Controls' (
('AnalogClockWindow' 'WxAnalogClockWindowDemo')
('ColourSelect' 'WxColourSelectDemo')
('Editor' 'WxEditorDemo')
('GenericButtons' 'WxGenericButtonsDemo')
('GenericDirCtrl' 'WxGenericDirCtrlDemo')
('LEDNumberCtrl' 'WxLEDNumberCtrlDemo')
('MultiSash' 'WxMultiSashDemo')
('PopupControl' 'WxPopupControlDemo')
('ColourChooser' 'WxColourChooserDemo')
('TreeListCtrl' 'WxTreeListCtrlDemo')
))
"
( 'More Windows/Controls' (
('CalendarCtrl' 'WxCalendarCtrlDemo' ('WxCalendarCtrl') )
('ContextHelp' 'WxContextHelpDemo' ('WxContextHelp') )
('MimeTypesManager' 'WxMimeTypesManagerDemo' ('WxMimeTypesManager') )
('StyledTextCtrl1' 'WxStyledTextCtrlDemo1' ('WxStyledTextCtrl') )
('VListBox' 'WxVListBoxDemo' ('WxVListBox') )
" ('ActiveXFlashWindow' 'ActiveXFlashWindowDemo')
('ActiveXIEHtmlWindow' 'ActiveXIEHtmlWindowDemo')
('ActiveXPDFWindow' 'ActiveXPDFWindowDemo')
('Calendar' 'CalendarDemo')
('DynamicSashWindow' 'DynamicSashWindowDemo')
('EditableListBox' 'EditableListBoxDemo')
('FancyText' 'FancyTextDemo')
('FileBrowseButton' 'FileBrowseButtonDemo')
('FloatBar' 'FloatBarDemo')
('FloatCanvas' 'FloatCanvasDemo')
('HtmlWindow' 'HtmlWindowDemo')
('IntCtrl' 'IntCtrlDemo')
('MVCTree' 'MVCTreeDemo')
('MaskedEditControls' 'MaskedEditControlsDemo')
('MaskedNumCtrl' 'MaskedNumCtrlDemo')
('ScrolledPanel' 'ScrolledPanelDemo')
('SplitTree' 'SplitTreeDemo')
('StyledTextCtrl2' 'WxStyledTextCtrlDemo2')
('TablePrint' 'TablePrintDemo')
('Throbber' 'ThrobberDemo')
('TimeCtrl' 'TimeCtrlDemo')
"
))
( 'Window Layout' (
('GridBagSizer' 'WxGridBagSizerDemo' ('WxGridBagSizer') )
('LayoutConstraints' 'WxLayoutConstraintsDemo' ('WxLayoutConstraints') )
('Sizers' 'WxSizersDemo' ('WxSizersDemoTestFrame' 'WxSizersDemoSampleWindow' 'WxSizer' 'WxBoxSizer' 'WxGridSizer' 'WxFlexGridSizer') )
('XmlResource' 'WxXmlResourceDemo' ('WxXmlResource') )
('XmlResourceHandler' 'WxXmlResourceHandlerDemo' ('WxXmlResourceCustomHandler' 'WxXmlCustomPanel') )
('XmlResourceSubclass' 'WxXmlResourceSubclassDemo' ('WxXmlCustomPanel2') )
"('LayoutAnchors' 'WxLayoutAnchorsDemo')
('Layoutf' 'WxLayoutfDemo')
('RowColSizer' 'WxRowColSizerDemo')
('ScrolledPanel' 'WxScrolledPanelDemo')"
))
"
( 'Processes and Events' (
('EventManager' 'WxEventManagerDemo')
('KeyEvents' 'WxKeyEventsDemo')
('Process' 'WxProcessDemo')
('CustomEvents' 'WxCustomEventsDemo')
('Threads' 'WxThreadsDemo')
('Timer' 'WxTimerDemo')
))
( 'Clipboard and DnD' (
('CustomDragAndDrop' 'WxCustomDragAndDropDemo')
('DragAndDrop' 'WxDragAndDropDemo')
('URLDragAndDrop' 'WxURLDragAndDropDemo')
))
"
( 'Using Images' (
('ArtProvider' 'WxArtProviderDemo' ('WxArtProvider') )
('Cursor' 'WxCursorDemo' ('WxCursor') )
('DragImage' 'WxDragImageSampleCanvas' ('WxDragImageSample' 'WxDragShape') )
('Image' 'WxImageDemo' ('WxImage') )
('ImageAlpha' 'WxImageAlphaDemo' ('WxImage') )
('Mask' 'WxMaskDemo' ('WxImage') )
"('ImageFromStream' 'WxImageFromStreamDemo')
('Throbber' 'WxThrobberDemo')"
))
( 'Miscellaneous' (
('DrawXXXList' 'WxDrawXXXListDemo' ('WxDC') )
('FileHistory' 'WxFileHistoryDemo' ('WxFileHistory') )
('FontEnumerator' 'WxFontEnumeratorDemo' ('WxFontEnumerator') )
('PrintFramework' 'WxPrintFrameworkDemo' ('WxDemoPrintout') )
('ShapedWindow' 'WxShapedWindowDemo' ('WxFrame') )
"('ColourDB' 'WxColourDBDemo')
('Joystick' 'WxJoystickDemo')
('OGL' 'WxOGLDemo')
('Sound' 'WxSoundDemo')
('Unicode' 'WxUnicodeDemo')"
))
( 'Other Samples' (
('Minimal' 'WxMinimalSample')
('AboutBox' 'WxHtmlAboutSample' ('WxHtmlWindow') )
('Deferred Events' 'WxDeferredButtonDemo' )
('Dialogs' 'WxDialogSample' ('WxDialogSampleCanvas') )
('Controls' 'WxControlsSample' ('WxControl') )
('HelpController' 'WxHelpControllerDemo' ('WxHelpController') )
"('DragImage2' 'WxDragImageSample' ('WxDragImageSampleCanvas' 'WxDragShape') )
('StyledText' 'WxSTCSample' ('WxStyledTextCtrl') )
('SplitterWindow2' 'WxSplitterSample' ('WxSplitterTestCanvas') )"
('SqueakDevTools' 'WxDevToolsSample' ('BrowserPresenter') )
('XRC Sample' 'WxXrcDemoFrame' ('WxXrcDemoPreferencesDialog' 'WxXrcDemoResizableListCtrl') )
('XRC Viewer' 'WxXrcViewerDemo' ('XMLDOMParser') )
))
)! !
!WxEvtHandler class methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 10:15'!
deferredEvents
^ DeferredEvents ifNil: [ DeferredEvents := SharedQueue new ]! !
!WxEvtHandler class methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 10:16'!
initialize
DeferredEvents := nil.
self initializeSynonyms.
self initializeCompositeEvents.
self initializeEventSymbols.
self initializeEventClasses.
! !
!WxEvtHandler class methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 18:28'!
processAction: anArray
| obj action evt |
evt := anArray first.
obj := anArray second.
action := anArray third.
action isBlock ifTrue: [
"special case of a block context"
action value: evt.
^true
].
(action class == Array) ifTrue: [
"special case of a selector-arg pair (see WxMenu>>add:target:selector:argument)"
obj perform: action first with: action second.
^true
].
action isUnary ifTrue: [
obj perform: action.
]
ifFalse: [
obj perform: action with: evt.
].! !
!WxEvtHandler class methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 10:16'!
runDeferredEventProcess
DeferredEventProcess ifNotNil: [ self stopDeferredEventProcess ].
DeferredEventProcess :=
[
[ true ] whileTrue: [
self processAction: self deferredEvents next.
].
] forkAt: Processor userSchedulingPriority.
! !
!WxEvtHandler class methodsFor: 'as yet unclassified' stamp: 'rtg 2/23/2005 08:27'!
runEventProcess
EventProcess ifNotNil: [ self stopEventProcess ].
EventProcess :=
[
[ true ] whileTrue: [
(Delay forMilliseconds: 5) wait.
[ Wx processEvents == true ] whileTrue: [].
].
] forkAt: Processor userSchedulingPriority.
! !
!WxEvtHandler class methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 21:47'!
startingUp: resuming
self runEventProcess.
self startCallbackProcesses.
self runDeferredEventProcess.
! !
!WxEvtHandler class methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 21:45'!
stopDeferredEventProcess
DeferredEventProcess ifNotNil: [ DeferredEventProcess terminate ].
DeferredEventProcess := nil.! !
!WxDeferredButtonDemo class methodsFor: 'as yet unclassified' stamp: 'rtg 2/22/2005 22:05'!
testPanel: parent
^(self parent: parent) build
! !
!WxWindowDisabler class methodsFor: 'generated' stamp: 'rtg 2/23/2005 08:39'!
disableEventsWhile: aBlock
| disabler |
disabler := self new.
[ aBlock value ] ensure: [ disabler delete ].! !
WxEvtHandler initialize!
!WxEvtHandler class reorganize!
('as yet unclassified' callbackProcesses checkCallbackException cloneCurrentEvent compositeEvents convertWxEventNameToSymbol: deferredEvents eventClassForEventType:eventObject: eventClasses eventDeleted: eventProcess eventSymbols initialize initializeCompositeEvents initializeEventClasses initializeEventSymbols initializeSynonyms processAction: processEvent runCallbackProcess: runDeferredEventProcess runEventProcess showEvents showEvents: shuttingDown: startCallbackProcesses startingUp: stopCallbackProcess: stopCallbackProcesses stopDeferredEventProcess stopEventProcess symbolToValue: synonyms valueToSymbol:)
('generated' new)
!
WxEvtHandler startingUp: true.!
Loading...