'From Squeak5.1 of 20 May 2016 [latest update: #15982] on 21 May 2016 at 10:09:53 am'! Morph subclass: #HandMorph instanceVariableNames: 'mouseFocus keyboardFocus eventListeners mouseListeners keyboardListeners mouseClickState mouseOverHandler lastMouseEvent targetOffset damageRecorder cacheCanvas cachedCanvasHasHoles temporaryCursor temporaryCursorOffset hardwareCursor hasChanged savedPatch userInitials lastEventBuffer genieGestureProcessor keyboardInterpreter mouseCaptureFilters keyboardFilters eventCaptureFilters mouseFilters eventFilters keyboardCaptureFilters ' classVariableNames: 'CompositionWindowManager DoubleClickTime DragThreshold EventStats NewEventRules NormalCursor PasteBuffer ShowEvents ' poolDictionaries: 'EventSensorConstants' category: 'Morphic-Kernel'! Object subclass: #PluggableEventFilter instanceVariableNames: 'filterBlock' classVariableNames: '' poolDictionaries: '' category: 'Morphic-Events'! MorphicEvent subclass: #UserInputEvent instanceVariableNames: 'type buttons position handler wasHandled wasIgnored ' classVariableNames: '' poolDictionaries: 'EventSensorConstants' category: 'Morphic-Events'! !Object methodsFor: '*Morphic-Events-Filtering' stamp: 'mt 5/20/2016 13:57'! filterEvent: anEvent for: aMorphOrNil "Get the chance to intercept the event. Note that global event filters might not be provided with the actual target. Returns the event that should be processed any further. If you want to ignore them, set #wasIgnored: to true." "anEvent sentTo: self" "<-- all filters are basically event listener???" ^ anEvent "no change"! ! !Morph methodsFor: 'events-processing' stamp: 'mt 5/21/2016 09:53'! handleEvent: anEvent "Apply event filters and then handle the resulting event. We have to return the event to chain filters." | filteredEvent | filteredEvent := self sendFilterEventBubble: anEvent for: self. filteredEvent wasIgnored ifFalse: [filteredEvent sentTo: self]. ^ filteredEvent! ! !Morph methodsFor: 'events-processing' stamp: 'mt 5/21/2016 09:57'! processEvent: anEvent using: defaultDispatcher "This is the central entry for dispatching events in morphic. Given some event and a default dispatch strategy, find the right receiver and let him handle it. WARNING: This is a powerful hook. If you want to use a different event dispatcher from the default, here is the place to hook it in. Depending on how the dispatcher is written (e.g., whether it calls simply #processEvent: or #processEvent:using:) you can change the dispatch strategy for entire trees of morphs. Similarly, you can disable entire trees of morphs from receiving any events whatsoever. Read the documentation in class MorphicEventDispatcher before playing with it. Event filters for this capture phase can both influence and overrule the way the receiver can reject the event. If the filter ignores the event, the whole event procecssing will stop. If the filtered event is still not ignored, the receiver can decide to still reject it end regular event dispatch will go on." | filteredEvent | filteredEvent := self sendFilterEventCapture: anEvent for: self. filteredEvent wasIgnored ifTrue: [^ filteredEvent]. (self rejectsEvent: filteredEvent) ifTrue:[^#rejected]. ^defaultDispatcher dispatchEvent: filteredEvent with: self! ! !Morph methodsFor: 'events-processing' stamp: 'mt 5/21/2016 09:46'! sendFilterEvent: anEvent for: anObject to: filterGroup "An event gets filtered if some event filter wants to filter it." | filteredEvent | filterGroup ifNil: [^anEvent]. filteredEvent := anEvent. filterGroup do: [:filterOrNil | filterOrNil ifNotNil: [:filter | filteredEvent := filter filterEvent: filteredEvent for: anObject]]. ^ filteredEvent! ! !Morph methodsFor: 'events-processing' stamp: 'mt 5/21/2016 09:46'! sendFilterEventBubble: anEvent for: anObject "Apply event bubbling filters." ^ self sendFilterEventBubbleAgain: (self sendFilterEvent: anEvent for: anObject to: self eventBubbleFilters) for: anObject! ! !Morph methodsFor: 'events-processing' stamp: 'mt 5/21/2016 09:46'! sendFilterEventBubbleAgain: anEvent for: anObject "Apply keyboard-specific and mouse-specific bubbling filters. If a filter changes the event type, filter again. WARNING: This is a powerful mechanism. Filters can create endless loops, which are difficult to debug." | filteredEvent | filteredEvent := anEvent. filteredEvent isKeyboard ifTrue: [ filteredEvent := self sendFilterEvent: filteredEvent for: anObject to: self keyboardBubbleFilters. filteredEvent isKeyboard not ifTrue: [^ self sendFilterEventBubbleAgain: filteredEvent for: anObject]]. filteredEvent isMouse ifTrue: [ filteredEvent := self sendFilterEvent: filteredEvent for: anObject to: self mouseBubbleFilters. filteredEvent isMouse not ifTrue: [^ self sendFilterEventBubbleAgain: filteredEvent for: anObject]]. ^ filteredEvent! ! !Morph methodsFor: 'events-processing' stamp: 'mt 5/21/2016 09:46'! sendFilterEventCapture: anEvent for: anObject "Apply event capturing filters." ^ self sendFilterEventCaptureAgain: (self sendFilterEvent: anEvent for: anObject to: self eventCaptureFilters) for: anObject! ! !Morph methodsFor: 'events-processing' stamp: 'mt 5/21/2016 09:46'! sendFilterEventCaptureAgain: anEvent for: anObject "Apply keyboard-specific and mouse-specific capturing filters. If a filter changes the event type, filter again. WARNING: This is a powerful mechanism. Filters can create endless loops, which are difficult to debug." | filteredEvent | filteredEvent := anEvent. filteredEvent isKeyboard ifTrue: [ filteredEvent := self sendFilterEvent: filteredEvent for: anObject to: self keyboardCaptureFilters. filteredEvent isKeyboard not ifTrue: [^ self sendFilterEventCaptureAgain: filteredEvent for: anObject]]. filteredEvent isMouse ifTrue: [ filteredEvent := self sendFilterEvent: filteredEvent for: anObject to: self mouseCaptureFilters. filteredEvent isMouse not ifTrue: [^ self sendFilterEventCaptureAgain: filteredEvent for: anObject]]. ^ filteredEvent! ! !Morph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:07'! addEventCaptureFilter: anObject self eventCaptureFilters: (self addFilter: anObject to: self eventCaptureFilters).! ! !Morph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:08'! addKeyboardCaptureFilter: anObject self keyboardCaptureFilters: (self addFilter: anObject to: self keyboardCaptureFilters).! ! !Morph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:08'! addMouseCaptureFilter: anObject self mouseCaptureFilters: (self addFilter: anObject to: self mouseCaptureFilters).! ! !Morph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:05'! eventCaptureFilters ^ self valueOfProperty: #eventCaptureFilters! ! !Morph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:05'! eventCaptureFilters: anArrayOrNil ^ self setProperty: #eventCaptureFilters toValue: anArrayOrNil! ! !Morph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:06'! keyboardCaptureFilters ^ self valueOfProperty: #keyboardCaptureFilters! ! !Morph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:06'! keyboardCaptureFilters: anArrayOrNil ^ self setProperty: #keyboardCaptureFilters toValue: anArrayOrNil! ! !Morph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:06'! mouseCaptureFilters ^ self valueOfProperty: #mouseCaptureFilters! ! !Morph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:07'! mouseCaptureFilters: anArrayOrNil ^ self setProperty: #mouseCaptureFilters toValue: anArrayOrNil! ! !Morph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:09'! removeEventCaptureFilter: anObject self eventCaptureFilters: (self removeFilter: anObject from: self eventCaptureFilters).! ! !Morph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:09'! removeKeyboardCaptureFilter: anObject self keyboardCaptureFilters: (self removeFilter: anObject from: self keyboardCaptureFilters).! ! !Morph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:10'! removeMouseCaptureFilter: anObject self mouseCaptureFilters: (self removeFilter: anObject from: self mouseCaptureFilters).! ! !Morph methodsFor: 'events-filtering-bubbling' stamp: 'mt 5/21/2016 09:08'! addEventBubbleFilter: anObject self eventBubbleFilters: (self addFilter: anObject to: self eventBubbleFilters).! ! !Morph methodsFor: 'events-filtering-bubbling' stamp: 'mt 5/21/2016 09:08'! addKeyboardBubbleFilter: anObject self keyboardBubbleFilters: (self addFilter: anObject to: self keyboardBubbleFilters).! ! !Morph methodsFor: 'events-filtering-bubbling' stamp: 'mt 5/21/2016 09:08'! addMouseBubbleFilter: anObject self mouseBubbleFilters: (self addFilter: anObject to: self mouseBubbleFilters).! ! !Morph methodsFor: 'events-filtering-bubbling' stamp: 'mt 5/21/2016 09:05'! eventBubbleFilters ^ self valueOfProperty: #eventBubbleFilters! ! !Morph methodsFor: 'events-filtering-bubbling' stamp: 'mt 5/21/2016 09:05'! eventBubbleFilters: anArrayOrNil ^ self setProperty: #eventBubbleFilters toValue: anArrayOrNil! ! !Morph methodsFor: 'events-filtering-bubbling' stamp: 'mt 5/21/2016 09:06'! keyboardBubbleFilters ^ self valueOfProperty: #keyboardBubbleFilters! ! !Morph methodsFor: 'events-filtering-bubbling' stamp: 'mt 5/21/2016 09:06'! keyboardBubbleFilters: anArrayOrNil ^ self setProperty: #keyboardBubbleFilters toValue: anArrayOrNil! ! !Morph methodsFor: 'events-filtering-bubbling' stamp: 'mt 5/21/2016 09:06'! mouseBubbleFilters ^ self valueOfProperty: #mouseBubbleFilters! ! !Morph methodsFor: 'events-filtering-bubbling' stamp: 'mt 5/21/2016 09:07'! mouseBubbleFilters: anArrayOrNil ^ self setProperty: #mouseBubbleFilters toValue: anArrayOrNil! ! !Morph methodsFor: 'events-filtering-bubbling' stamp: 'mt 5/21/2016 09:09'! removeEventBubbleFilter: anObject self eventBubbleFilters: (self removeFilter: anObject from: self eventBubbleFilters).! ! !Morph methodsFor: 'events-filtering-bubbling' stamp: 'mt 5/21/2016 09:09'! removeKeyboardBubbleFilter: anObject self keyboardBubbleFilters: (self removeFilter: anObject from: self keyboardBubbleFilters).! ! !Morph methodsFor: 'events-filtering-bubbling' stamp: 'mt 5/21/2016 09:10'! removeMouseBubbleFilter: anObject self mouseBubbleFilters: (self removeFilter: anObject from: self mouseBubbleFilters).! ! !Morph methodsFor: 'events-filtering' stamp: 'mt 5/17/2016 22:20'! addFilter: anObject to: aFilterGroup "Add anObject to the given listener group. Return the new group." | filters | filters := aFilterGroup. (filters notNil and:[filters includes: anObject]) ifFalse:[ filters ifNil:[filters := WeakArray with: anObject] ifNotNil:[filters := filters copyWith: anObject]]. filters := filters copyWithout: nil. "obsolete entries" ^filters! ! !Morph methodsFor: 'events-filtering' stamp: 'mt 5/17/2016 22:21'! removeFilter: anObject from: aFilterGroup "Remove anObject from the given listener group. Return the new group." | filters | aFilterGroup ifNil: [^nil]. filters := aFilterGroup. filters := filters copyWithout: anObject. filters := filters copyWithout: nil. "obsolete entries" filters isEmpty ifTrue: [filters := nil]. ^filters! ! !DockingBarMorph methodsFor: 'events-processing' stamp: 'mt 5/20/2016 13:16'! handleFocusEvent: evt "Handle focus events. Valid menu transitions are determined based on the menu currently holding the focus after the mouse went down on one of its children." | result filteredEvent | (evt isMouse and:[ evt isMouseUp ]) ifTrue:[^ evt]. result := self processEvent: evt. filteredEvent := result == #rejected ifTrue: [evt] ifFalse: [result]. "Need to handle keyboard input if we have the focus." filteredEvent isKeyboard ifTrue: [^ self handleEvent: filteredEvent]. "We need to handle button clicks outside and transitions to local popUps so throw away everything else" (filteredEvent isMouseOver or:[filteredEvent isMouse not]) ifTrue:[^filteredEvent]. "What remains are mouse buttons and moves" filteredEvent isMove ifFalse:[^self handleEvent: filteredEvent]. "handle clicks outside by regular means" "Now it's getting tricky. On #mouseMove we might transfer control to *either* the currently active submenu or the pop up owner, if any. Since the active sub menu is always displayed upfront check it first." selectedItem ifNotNil:[(selectedItem activateSubmenu: filteredEvent) ifTrue:[^filteredEvent]]. ! ! !HandMorph methodsFor: 'events-filtering-bubbling' stamp: 'mt 5/21/2016 09:14'! eventBubbleFilters Error signal: 'Hand morphs do only have capture filters. Install top-most bubble filters in the world.'! ! !HandMorph methodsFor: 'events-filtering-bubbling' stamp: 'mt 5/21/2016 09:14'! keyboardBubbleFilters Error signal: 'Hand morphs do only have capture filters. Install top-most bubble filters in the world.'! ! !HandMorph methodsFor: 'events-filtering-bubbling' stamp: 'mt 5/21/2016 09:14'! mouseBubbleFilters Error signal: 'Hand morphs do only have capture filters. Install top-most bubble filters in the world.'! ! !HandMorph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:11'! eventCaptureFilters ^ eventCaptureFilters! ! !HandMorph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:11'! eventCaptureFilters: anArrayOrNil eventCaptureFilters := anArrayOrNil! ! !HandMorph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:11'! keyboardCaptureFilters ^ keyboardCaptureFilters! ! !HandMorph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:12'! keyboardCaptureFilters: anArrayOrNil keyboardCaptureFilters := anArrayOrNil! ! !HandMorph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:12'! mouseCaptureFilters ^ mouseCaptureFilters! ! !HandMorph methodsFor: 'events-filtering-capturing' stamp: 'mt 5/21/2016 09:12'! mouseCaptureFilters: anArrayOrNil mouseCaptureFilters := anArrayOrNil! ! !HandMorph methodsFor: 'events-processing' stamp: 'mt 5/21/2016 09:50'! handleEvent: unfilteredEvent | filteredEvent | owner ifNil: [^ unfilteredEvent "not necessary but good style -- see Morph >> #handleEvent:"]. self logEvent: unfilteredEvent. "Mouse-over events occur really, really, really often. They are kind of the heart beat of the Morphic UI process." unfilteredEvent isMouseOver ifTrue: [^ self sendMouseEvent: unfilteredEvent]. self showEvent: unfilteredEvent. self sendListenEvents: unfilteredEvent. filteredEvent := self sendFilterEventCapture: unfilteredEvent for: nil. "filteredEvent := unfilteredEvent" " <-- use this to disable global capture filters" filteredEvent wasIgnored ifTrue: [ self mouseOverHandler processMouseOver: lastMouseEvent. ^ filteredEvent]. filteredEvent isWindowEvent ifTrue: [ self sendEvent: filteredEvent focus: nil. self mouseOverHandler processMouseOver: lastMouseEvent. ^ filteredEvent]. filteredEvent isKeyboard ifTrue:[ self sendKeyboardEvent: filteredEvent. self mouseOverHandler processMouseOver: lastMouseEvent. ^ filteredEvent]. filteredEvent isDropEvent ifTrue:[ self sendEvent: filteredEvent focus: nil. self mouseOverHandler processMouseOver: lastMouseEvent. ^ filteredEvent]. filteredEvent isMouse ifFalse: [ self mouseOverHandler processMouseOver: lastMouseEvent. ^ filteredEvent]. " ********** MOUSE EVENT *********** " lastMouseEvent := filteredEvent. "Check for pending drag or double click operations." mouseClickState ifNotNil:[ (mouseClickState handleEvent: filteredEvent from: self) ifFalse:[ "Possibly dispatched #click: or something and will not re-establish otherwise" self mouseOverHandler processMouseOver: lastMouseEvent. ^ filteredEvent]]. filteredEvent isMove ifTrue:[ self position: filteredEvent position. self sendMouseEvent: filteredEvent. self mouseOverHandler processMouseOver: lastMouseEvent. ^ filteredEvent]. "Issue a synthetic move event if we're not at the position of the event" filteredEvent position = self position ifFalse: [self moveToEvent: filteredEvent]. "Drop submorphs on button events" self hasSubmorphs ifTrue:[self dropMorphs: filteredEvent] ifFalse:[self sendMouseEvent: filteredEvent]. self mouseOverHandler processMouseOver: lastMouseEvent. ^ filteredEvent "not necessary but good style -- see Morph >> #handleEvent:" ! ! !HandMorph methodsFor: 'events-processing' stamp: 'mt 5/21/2016 09:47'! sendFilterEventCaptureAgain: anEvent for: anObjectOrNil "Apply keyboard-specific and mouse-specific capturing filters. If a filter changes the event type, filter again. WARNING: This is a powerful mechanism. Filters can create endless loops, which are difficult to debug. Overwritten to provide keyboard and mouse focus holders to event filters. Note that hence the event target in such global filters can be nil." | filteredEvent | filteredEvent := anEvent. filteredEvent isKeyboard ifTrue: [ filteredEvent := self sendFilterEvent: filteredEvent for: self keyboardFocus to: self keyboardCaptureFilters. filteredEvent isKeyboard not ifTrue: [^ self sendFilterEventCaptureAgain: filteredEvent for: anObjectOrNil]]. filteredEvent isMouse ifTrue: [ filteredEvent := self sendFilterEvent: filteredEvent for: self mouseFocus to: self mouseCaptureFilters. filteredEvent isMouse not ifTrue: [^ self sendFilterEventCaptureAgain: filteredEvent for: anObjectOrNil]]. ^ filteredEvent! ! !HandMorph methodsFor: 'events-debugging' stamp: 'mt 5/20/2016 13:44'! logEvent: anEvent "Update statistics for processed events." EventStats ifNil:[EventStats := IdentityDictionary new]. EventStats at: #count put: (EventStats at: #count ifAbsent:[0]) + 1. EventStats at: anEvent type put: (EventStats at: anEvent type ifAbsent:[0]) + 1.! ! !HandMorph methodsFor: 'events-debugging' stamp: 'mt 5/20/2016 13:48'! showEvent: anEvent "Show details about the event on the display form. Useful for debugging." | ofs | ShowEvents == true ifFalse: [^ self]. Display fill: (0@0 extent: 300@120) rule: Form over fillColor: Color white. ofs := (owner hands indexOf: self) - 1 * 60. anEvent isKeyboard ifTrue: [ 'key: ', anEvent printString displayAt: (0@ofs) + (0@30) ] ifFalse: [ 'evt ', anEvent printString displayAt: (0@ofs) + (0@0) ]. 'kf: ', self keyboardFocus printString displayAt: (0@ofs)+(0@45). 'mf: ', self mouseFocus printString displayAt: (0@ofs) + (0@15) ! ! !HandMorph methodsFor: 'grabbing/dropping' stamp: 'mt 5/21/2016 09:58'! dropMorph: aMorph event: anEvent "Drop the given morph which was carried by the hand" | event dropped | (anEvent isMouseUp and:[aMorph shouldDropOnMouseUp not]) ifTrue:[^self]. "Note: For robustness in drag and drop handling we remove the morph BEFORE we drop him, but we keep his owner set to the hand. This prevents system lockups when there is a problem in drop handling (for example if there's an error in #wantsToBeDroppedInto:). THIS TECHNIQUE IS NOT RECOMMENDED FOR CASUAL USE." self privateRemove: aMorph. aMorph privateOwner: self. dropped := aMorph. (dropped hasProperty: #addedFlexAtGrab) ifTrue:[dropped := aMorph removeFlexShell]. event := DropEvent new setPosition: self position contents: dropped hand: self. [ "In case of an error, ensure that the morph-to-be-dropped will be disposed. Otherwise it may confuse garbage handler. See the sends of #privateRemove: and #privateOwner: above." event := self sendEvent: event focus: nil. "event filters can apply and filtered events will be returned" event wasHandled ifFalse: [aMorph rejectDropMorphEvent: event] ] ensure: [ aMorph owner == self ifTrue: [aMorph delete] ]. self mouseOverHandler processMouseOver: anEvent.! ! !HandMorph methodsFor: 'private events' stamp: 'mt 5/20/2016 13:21'! sendEvent: anEvent focus: focusHolder clear: aBlock "Send the event to the morph currently holding the focus, or if none to the owner of the hand." | result | focusHolder ifNotNil:[^self sendFocusEvent: anEvent to: focusHolder clear: aBlock]. ActiveEvent := anEvent. [result := owner processEvent: anEvent] ensure: [ActiveEvent := nil]. ^ result == #rejected ifTrue: [anEvent] ifFalse: [result "filtered event"]! ! !HandMorph methodsFor: 'private events' stamp: 'mt 5/20/2016 13:20'! sendFocusEvent: anEvent to: focusHolder clear: aBlock "Send the event to the morph currently holding the focus" | result w | w := focusHolder world ifNil:[aBlock value. ^ anEvent]. w becomeActiveDuring:[ ActiveHand := self. ActiveEvent := anEvent. result := focusHolder handleFocusEvent: (anEvent transformedBy: (focusHolder transformedFrom: self)). ]. ^result == #rejected ifTrue: [anEvent] ifFalse: [result "filtered event"]! ! !HandMorph methodsFor: 'private events' stamp: 'mt 5/20/2016 14:01'! sendListenEvents: anEvent "Send the given event to all registered event listeners." self sendListenEvent: anEvent to: self eventListeners. anEvent isKeyboard ifTrue: [self sendListenEvent: anEvent to: self keyboardListeners]. anEvent isMouse ifTrue: [self sendListenEvent: anEvent to: self mouseListeners].! ! !MenuMorph methodsFor: 'events' stamp: 'mt 5/20/2016 12:50'! handleFocusEvent: evt "Handle focus events. Valid menu transitions are determined based on the menu currently holding the focus after the mouse went down on one of its children." | result filteredEvent | result := self processEvent: evt. filteredEvent := result == #rejected ifTrue: [evt] ifFalse: [result]. "Need to handle keyboard input if we have the focus." filteredEvent isKeyboard ifTrue: [^ self handleEvent: filteredEvent]. "We need to handle button clicks outside and transitions to local popUps so throw away everything else" (filteredEvent isMouseOver or:[filteredEvent isMouse not]) ifTrue:[^filteredEvent]. "What remains are mouse buttons and moves" filteredEvent isMove ifFalse:[^self handleEvent: filteredEvent]. "handle clicks outside by regular means" "Now it's getting tricky. On #mouseMove we might transfer control to *either* the currently active submenu or the pop up owner, if any. Since the active sub menu is always displayed upfront check it first." selectedItem ifNotNil: [ (selectedItem activateSubmenu: filteredEvent) ifTrue: [ ^filteredEvent ] ifFalse: [ (self containsPoint: filteredEvent position) ifFalse: [ self selectItem: nil event: filteredEvent ] ] ]. "Note: The following does not traverse upwards but it's the best I can do for now" popUpOwner ifNotNil:[(popUpOwner activateOwnerMenu: filteredEvent) ifTrue:[^filteredEvent]]. ^ filteredEvent! ! !MorphicEvent methodsFor: 'accessing' stamp: 'mt 5/19/2016 13:12'! wasIgnored ^ false! ! !MorphicEvent methodsFor: 'accessing' stamp: 'mt 5/19/2016 13:12'! wasIgnored: boolean ! ! !MorphicEvent methodsFor: 'dispatching' stamp: 'mt 5/21/2016 10:06'! ignore self wasIgnored: true.! ! !MorphicEvent methodsFor: 'transforming' stamp: 'mt 5/20/2016 10:12'! translateTo: point ! ! !DropEvent methodsFor: 'transforming' stamp: 'mt 5/20/2016 10:12'! translateTo: point position := point.! ! !MorphicEventDispatcher methodsFor: 'private' stamp: 'mt 5/20/2016 14:43'! dispatchEvent: anEvent toSubmorphsOf: aMorph "Dispatch the given event to the submorphs of the given morph. --- PRIVATE!!" | filteredEvent | aMorph submorphsDo: [:child | filteredEvent := child processEvent: (anEvent transformedBy: (child transformedFrom: aMorph)) using: self. "use same dispatcher" filteredEvent == #rejected ifFalse: [ "filteredEvent or #rejected" filteredEvent translateTo: anEvent position. "restore to untransformed coordinates" filteredEvent wasHandled ifFalse: [filteredEvent copyHandlerState: anEvent]. "restore handler if needed" self flag: #overlappingChildren. "mt: We cannot give two overlapping siblings the chance to handle the event!!" ^ filteredEvent]]. ^ #rejected! ! !MorphicEventDispatcher methodsFor: 'private' stamp: 'mt 5/20/2016 15:12'! dispatchEvent: anEvent withHandler: aHandler withMorph: aMorph "Perform the actual event dispatch. Use the given object as handler. Ask submorphs first to handle the event. Then bubble up. Stop if ignored. Note that event rejection and event filters are two separete concepts. Filters come from the outside. Rejection is a morph's decision. * The top-most chain of visible, unlocked morphs containing the event position will get a chance to handle the event. * When travelling up, the prospective handler is always executed. The handler needs to check if the event was handled before as well as checking if somebody else's handler has been installed. * If another handler has been installed but the event was not handled it means that somebody up in the hierarchy wants to handle the event." | result filteredEvent | result := self dispatchEvent: anEvent toSubmorphsOf: aMorph. result == #rejected "Anybody?" ifFalse: [filteredEvent := result] ifTrue: [ "My submorphs did not want it. Do I want it anyway? It's about locked children..." (aMorph containsPoint: anEvent position event: anEvent) ifFalse: [^ #rejected]. filteredEvent := anEvent "there was no filtering, only basic rejects"]. "Receiver is in the top-most unlocked, visible chain." (aHandler notNil and: [filteredEvent wasIgnored not]) ifTrue: [filteredEvent := aHandler handleEvent: filteredEvent]. ^ filteredEvent! ! !MorphicEventDispatcher methodsFor: 'specific' stamp: 'mt 5/20/2016 15:21'! dispatchDefault: anEvent with: aMorph "Dispatch the given event. The event will be passed to the front-most visible submorph that contains the position wrt. to the event." "Try to get out quickly" anEvent wasIgnored ifTrue: [^anEvent "propagate the ignored event"]. (aMorph fullBounds containsPoint: anEvent position) ifFalse:[^#rejected]. ^ self dispatchEvent: anEvent withHandler: aMorph withMorph: aMorph! ! !MorphicEventDispatcher methodsFor: 'specific' stamp: 'mt 5/20/2016 15:21'! dispatchDropEvent: anEvent with: aMorph "Find the appropriate receiver for the event and let it handle it. The dispatch is similar to the default dispatch with one difference: Morphs are given the chance to reject an entire drop operation. If the operation is rejected, no drop will be executed." "Try to get out quickly" anEvent wasIgnored ifTrue: [^anEvent "propagate the ignored event"]. (aMorph fullBounds containsPoint: anEvent position) ifFalse:[^#rejected]. "Give aMorph a chance to repel the dropping morph" aMorph rejectDropEvent: anEvent. anEvent wasHandled ifTrue:[^anEvent]. ^ self dispatchEvent: anEvent withHandler: aMorph withMorph: aMorph! ! !MorphicEventDispatcher methodsFor: 'specific' stamp: 'mt 5/20/2016 15:20'! dispatchMouseDown: anEvent with: aMorph "Find the appropriate receiver for the event and let it handle it. When travelling down the hierarchy a prospective handler for the event is installed. This prospective handler can be used by submorphs wishing to handle the mouse down for negotiating who the receiver is." | handler lastHandler | "Try to get out quickly" anEvent wasIgnored ifTrue: [^anEvent "propagate the ignored event"]. (aMorph fullBounds containsPoint: anEvent position) ifFalse:[^#rejected]. "In case the mouse wasn't even in the receiver" lastHandler := anEvent handler. "Negotiate and install the actual handler." handler := aMorph handlerForMouseDown: anEvent. handler ifNotNil: [anEvent handler: handler]. [^ self dispatchEvent: anEvent withHandler: handler withMorph: aMorph ] ensure: [ anEvent handler: lastHandler. "good style"].! ! !MorphicEventDispatcher methodsFor: 'specific' stamp: 'mt 5/20/2016 15:19'! dispatchWindowEvent: anEvent with: aMorph "Host window events do not have a position and are only dispatched to the World" anEvent wasIgnored ifTrue: [^anEvent]. aMorph isWorldMorph ifFalse: [^#rejected]. anEvent wasHandled ifTrue:[^anEvent]. ^aMorph handleEvent: anEvent! ! !PasteUpMorph methodsFor: 'events-processing' stamp: 'mt 5/21/2016 09:29'! processEvent: anEvent using: defaultDispatcher "Reimplemented to install the receiver as the new ActiveWorld if it is one" | priorWorld result | self isWorldMorph ifFalse:[^super processEvent: anEvent using: defaultDispatcher]. priorWorld := ActiveWorld. ActiveWorld := self. [result := super processEvent: anEvent using: defaultDispatcher] ensure: [ActiveWorld := priorWorld]. ^result ! ! !PluggableEventFilter methodsFor: 'accessing' stamp: 'mt 5/17/2016 22:44'! filterBlock ^ filterBlock! ! !PluggableEventFilter methodsFor: 'accessing' stamp: 'mt 5/17/2016 22:44'! filterBlock: anObject filterBlock := anObject! ! !PluggableEventFilter methodsFor: 'events-filtering' stamp: 'mt 5/19/2016 11:49'! filterEvent: event for: target ^ self filterBlock ifNil: [event] ifNotNil: [:b | b cull: event cull: target]! ! !PluggableEventFilter class methodsFor: 'instance creation' stamp: 'mt 5/17/2016 22:48'! on: filterBlock ^ self new filterBlock: filterBlock! ! !SystemWindow methodsFor: 'events' stamp: 'mt 5/20/2016 13:11'! mouseDown: evt | wasKeyWindow result filteredEvent | (wasKeyWindow := self isKeyWindow) ifFalse: [ evt hand releaseKeyboardFocus. self beKeyWindow]. "If the window was locked, we did unlock it by now. If the user does not want to invest an additional click to interact with an actual widget, re-process the event." (wasKeyWindow not and: [model windowActiveOnFirstClick]) ifTrue: [ evt wasHandled: false. "force re-evaluation" result := self processEvent: evt. filteredEvent := result == #rejected ifTrue: [evt] ifFalse: [result]. evt becomeForward: filteredEvent. ^ self]. evt hand waitForClicksOrDrag: self event: evt selectors: { nil. nil. nil. #startDragFromLabel: } threshold: HandMorph dragThreshold.! ! !UserDialogBoxMorph methodsFor: 'constructing' stamp: 'mt 5/20/2016 13:10'! handleFocusEvent: evt "Handle focus events. Valid menu transitions are determined based on the menu currently holding the focus after the mouse went down on one of its children. Need to handle keyboard input if we have the focus." | result filteredEvent | result := self processEvent: evt. filteredEvent := result == #rejected ifTrue: [evt] ifFalse: [result]. ^ self handleEvent: filteredEvent! ! !UserInputEvent methodsFor: 'accessing' stamp: 'mt 5/19/2016 12:30'! wasIgnored ^ wasIgnored! ! !UserInputEvent methodsFor: 'accessing' stamp: 'mt 5/19/2016 12:30'! wasIgnored: aBoolean wasIgnored := aBoolean.! ! !UserInputEvent methodsFor: 'initialize' stamp: 'mt 5/19/2016 13:15'! initialize super initialize. wasIgnored := false.! ! !UserInputEvent methodsFor: 'transforming' stamp: 'mt 5/20/2016 10:11'! translateTo: point position := point.! ! MorphicEvent subclass: #UserInputEvent instanceVariableNames: 'type buttons position handler wasHandled wasIgnored' classVariableNames: '' poolDictionaries: 'EventSensorConstants' category: 'Morphic-Events'! !PluggableEventFilter reorganize! ('accessing' filterBlock filterBlock:) ('events-filtering' filterEvent:for:) ! !MorphicEventDispatcher reorganize! ('dispatching' dispatchEvent:with:) ('private' dispatchEvent:toSubmorphsOf: dispatchEvent:withHandler:withMorph:) ('specific' dispatchDefault:with: dispatchDropEvent:with: dispatchMouseDown:with: dispatchWindowEvent:with:) ! Morph subclass: #HandMorph instanceVariableNames: 'mouseFocus keyboardFocus eventListeners mouseListeners keyboardListeners eventFilters mouseFilters keyboardFilters eventCaptureFilters mouseCaptureFilters keyboardCaptureFilters mouseClickState mouseOverHandler lastMouseEvent targetOffset damageRecorder cacheCanvas cachedCanvasHasHoles temporaryCursor temporaryCursorOffset hardwareCursor hasChanged savedPatch userInitials lastEventBuffer genieGestureProcessor keyboardInterpreter' classVariableNames: 'CompositionWindowManager DoubleClickTime DragThreshold EventStats NewEventRules NormalCursor PasteBuffer ShowEvents' poolDictionaries: 'EventSensorConstants' category: 'Morphic-Kernel'! !HandMorph reorganize! ('accessing' anyButtonPressed colorForInsets lastEvent mouseOverHandler noButtonPressed targetOffset targetPoint userInitials userPicture userPicture:) ('balloon help' addBalloonHelp: balloonHelpList deleteBalloonTarget: removePendingBalloonFor: resetBalloonHelp: spawnBalloonFor: triggerBalloonFor:after:) ('events-filtering-bubbling' eventBubbleFilters keyboardBubbleFilters mouseBubbleFilters) ('events-filtering-capturing' eventCaptureFilters eventCaptureFilters: keyboardCaptureFilters keyboardCaptureFilters: mouseCaptureFilters mouseCaptureFilters:) ('caching' releaseCachedState) ('change reporting' invalidRect:from:) ('classification' isHandMorph) ('copying' veryDeepCopyWith:) ('cursor' cursorBounds showTemporaryCursor: showTemporaryCursor:hotSpotOffset: temporaryCursor) ('double click support' resetClickState waitForClicksOrDrag:event: waitForClicksOrDrag:event:selectors:threshold:) ('drawing' drawOn: fullDrawOn: hasChanged hasUserInformation needsToBeDrawn nonCachingFullDrawOn: restoreSavedPatchOn: savePatchFrom: shadowForm showHardwareCursor: updateCacheCanvas: visible:) ('drop shadows' shadowOffset) ('event handling' checkForMoreKeyboard cursorPoint flushEvents noticeMouseOver:event: processEvents) ('events-processing' handleEvent: isCapturingGesturePoints sendFilterEventCaptureAgain:for:) ('events-debugging' logEvent: showEvent:) ('focus handling' compositionWindowManager keyboardFocus keyboardFocus: mouseFocus mouseFocus: newKeyboardFocus: newMouseFocus: newMouseFocus:event: releaseAllFoci releaseKeyboardFocus releaseKeyboardFocus: releaseMouseFocus releaseMouseFocus:) ('genie-stubs' autoFocusRectangleBoundsFor: disableGenieFocus enableGenie focusStartEvent genieGestureProcessor isGenieAvailable isGenieEnabled isGenieFocused) ('geometry' position position: userInitials:andPicture:) ('grabbing/dropping' attachMorph: dropMorphs dropMorphs: dropMorph:event: grabMorph:from: targetOffset:) ('halo handling' halo: obtainHalo: releaseHalo: removeHalo removeHaloFromClick:on: removePendingHaloFor: spawnMagicHaloFor: triggerHaloFor:after:) ('halos and balloon help' halo) ('initialization' initForEvents initialize interrupted resourceJustLoaded) ('layout' fullBounds) ('events-listening' addEventListener: addKeyboardListener: addListener:to: addMouseListener: eventListeners eventListeners: keyboardListeners keyboardListeners: mouseListeners mouseListeners: removeEventListener: removeKeyboardListener: removeListener:from: removeMouseListener:) ('meta-actions' copyToPasteBuffer: grabMorph:) ('multilingual' clearKeyboardInterpreter keyboardInterpreter) ('objects from disk' objectForDataStream:) ('paste buffer' objectToPaste pasteBuffer pasteBuffer: pasteMorph) ('updating' changed) ('private events' generateDropFilesEvent: generateKeyboardEvent: generateMouseEvent: generateWindowEvent: mouseTrailFrom: moveToEvent: sendEvent:focus: sendEvent:focus:clear: sendFocusEvent:to:clear: sendKeyboardEvent: sendListenEvent:to: sendListenEvents: sendMouseEvent:) ('selected object' selectedObject) ('*Etoys-scripting' adaptedToWorld:) ('*Etoys-pen' trailMorph) ('*51Deprecated') ('*MorphicExtras-event handling' pauseEventRecorderIn:) ('gridded cursor' gridPointRaw gridTo:origin: griddedPoint: turnOffGridding) ! !Morph reorganize! ('accessing' actorStateOrNil actorState: adoptPaneColor: balloonText balloonText: balloonTextSelector balloonTextSelector: beFlap: beSticky beUnsticky borderColor borderColor: borderStyle borderStyleForSymbol: borderStyle: borderWidth borderWidthForRounding borderWidth: clearArea color colorForInsets color: couldHaveRoundedCorners defaultNameStemForInstances doesBevels eventHandler eventHandler: forwardDirection hasTranslucentColor highlight highlightColor highlightColor: insetColor isFlap isLocked isShared isSticky lock lock: methodCommentAsBalloonHelp modelOrNil player playerRepresented player: presenter raisedColor regularColor regularColor: rememberedColor rememberedColor: resistsRemoval resistsRemoval: scaleFactor setBorderStyle: sqkPage sticky: toggleLocked toggleResistsRemoval toggleStickiness unHighlight unlock unlockContents url userString viewBox visibleClearArea wantsToBeCachedByHand wantsToBeTopmost) ('accessing - extension' assureExtension extension hasExtension initializeExtension privateExtension: resetExtension) ('accessing - properties' hasProperty: otherProperties removeProperty: setProperties: setProperty:toValue: valueOfProperty: valueOfProperty:ifAbsentPut: valueOfProperty:ifAbsent: valueOfProperty:ifPresentDo:) ('caching' fullLoadCachedState fullReleaseCachedState loadCachedState releaseCachedState) ('change reporting' addedMorph: colorChangedForSubmorph: invalidRect: invalidRect:from: ownerChanged privateInvalidateMorph: userSelectedColor:) ('classification' demandsBoolean isAlignmentMorph isBalloonHelp isCompoundTileMorph isFlapOrTab isFlapTab isFlexMorph isHandMorph isKedamaMorph isModalShell isNumericReadoutTile isPhraseTileMorph isPlayfieldLike isRenderer isSoundTile isStandardViewer isStickySketchMorph isSyntaxMorph isTextMorph isTileMorph isTilePadMorph isViewer isWorldMorph isWorldOrHandMorph) ('connectors-scripting' wantsConnectorVocabulary) ('converting' asDraggableMorph asSnapshotThumbnail) ('copying' copy deepCopy duplicate duplicateMorphCollection: fullCopy updateReferencesUsing: usableSiblingInstance veryDeepCopyWith: veryDeepFixupWith: veryDeepInner:) ('creation' asMorph) ('debug and other' addDebuggingItemsTo:hand: addMouseActionIndicatorsWidth:color: addMouseUpAction addMouseUpActionWith: addViewingItemsTo: allStringsAfter: altSpecialCursor0 altSpecialCursor1 altSpecialCursor2 altSpecialCursor3 altSpecialCursor3: buildDebugMenu: defineTempCommand deleteAnyMouseActionIndicators inspectArgumentsPlayerInMorphic: inspectOwnerChain installModelIn: mouseUpCodeOrNil ownerChain programmedMouseDown:for: programmedMouseEnter:for: programmedMouseLeave:for: programmedMouseUp:for: removeMouseUpAction resumeAfterDrawError resumeAfterStepError tempCommand viewMorphDirectly) ('dispatching' disableSubmorphFocusForHand:) ('drawing' areasRemainingToFill: boundingBoxOfSubmorphs boundsWithinCorners changeClipSubmorphs clipLayoutCells clipLayoutCells: clippingBounds clipSubmorphs clipSubmorphs: doesOwnRotation drawDropHighlightOn: drawDropShadowOn: drawErrorOn: drawKeyboardFocusIndicationOn: drawMouseDownHighlightOn: drawOn: drawOverlayOn: drawRolloverBorderOn: drawSubmorphsOn: expandFullBoundsForDropShadow: expandFullBoundsForRolloverBorder: flashBounds fullDrawOn: hasClipSubmorphsString hide highlightedForMouseDown highlightForMouseDown highlightForMouseDown: imageForm imageFormDepth: imageFormForRectangle: imageFormWithout:andStopThere: imageForm:backgroundColor:forRectangle: imageForm:forRectangle: keyboardFocusColor refreshWorld shadowForm show updateDropShadowCache visible visible:) ('drop shadows' addDropShadow addDropShadowMenuItems:hand: changeShadowColor hasDropShadow hasDropShadowString hasDropShadow: hasRolloverBorder hasRolloverBorder: removeDropShadow setShadowOffset: shadowColor shadowColor: shadowOffset shadowOffset: shadowPoint: toggleDropShadow useSoftDropShadow useSoftDropShadow:) ('dropping/grabbing' aboutToBeGrabbedBy: disableDragNDrop dragEnabled dragEnabled: dragNDropEnabled dragSelectionColor dropEnabled dropEnabled: dropHighlightColor dropSuccessColor enableDragNDrop enableDragNDrop: enableDrag: enableDrop: formerOwner formerOwner: formerPosition formerPosition: grabTransform handledOwnDraggingBy:on: highlightedForDrop highlightForDrop highlightForDrop: justDroppedInto:event: justGrabbedFrom: morphToDropInPasteUp: nameForUndoWording rejectDropMorphEvent: repelsMorph:event: resetHighlightForDrop separateDragAndDrop slideBackToFormerSituation: slideToTrash: startDrag:with: toggleDragNDrop transportedMorph undoGrabCommand vanishAfterSlidingTo:event: wantsDroppedMorph:event: wantsToBeDroppedInto: wantsToBeOpenedInWorld willingToBeDiscarded) ('event handling' click click: cursorPoint doubleClickTimeout: doubleClick: dropFiles: firstClickTimedOut: handlerForMouseDown: handlerForYellowButtonDown: handlesKeyboard: handlesMouseDown: handlesMouseMove: handlesMouseOverDragging: handlesMouseOver: handlesMouseStillDown: hasFocus hasKeyboardFocus hasKeyboardFocus: hasMouseFocus hasMouseFocus: keyboardFocusChange: keyDown: keyStroke: keyUp: keyboardFocusDelegate mouseDown: mouseEnterDragging: mouseEnter: mouseLeaveDragging: mouseLeave: mouseMove: mouseStillDownThreshold mouseStillDown: mouseUp: moveOrResizeFromKeystroke: on:send:to: on:send:to:withValue: preferredKeyboardBounds preferredKeyboardPosition removeLink: restoreSuspendedEventHandler startDrag: suspendEventHandler tabAmongFields transformFromOutermostWorld transformFromWorld transformFrom: wantsDropFiles: wantsEveryMouseMove wantsKeyboardFocus wantsKeyboardFocusFor: wantsWindowEvents: windowEvent: wouldAcceptKeyboardFocus wouldAcceptKeyboardFocusUponTab yellowButtonActivity:) ('events-accessing' actionMap updateableActionMap) ('events-alarms' addAlarm:after: addAlarm:at: addAlarm:withArguments:after: addAlarm:withArguments:at: addAlarm:with:after: addAlarm:with:at: addAlarm:with:with:after: addAlarm:with:with:at: alarmScheduler removeAlarm: removeAlarm:at:) ('events-processing' containsPoint:event: defaultEventDispatcher handleDropFiles: handleDropMorph: handleEvent: handleFocusEvent: handleKeyDown: handleKeyUp: handleKeystroke: handleListenEvent: handleMouseDown: handleMouseEnter: handleMouseLeave: handleMouseMove: handleMouseOver: handleMouseStillDown: handleMouseUp: handleUnknownEvent: handleWindowEvent: mouseDownPriority mouseDownPriority: processEvent: processEvent:using: rejectDropEvent: rejectsEvent: sendFilterEvent:for:to: sendFilterEventBubble:for: sendFilterEventBubbleAgain:for: sendFilterEventCapture:for: sendFilterEventCaptureAgain:for: transformedFrom:) ('events-filtering-capturing' addEventCaptureFilter: addKeyboardCaptureFilter: addMouseCaptureFilter: eventCaptureFilters eventCaptureFilters: keyboardCaptureFilters keyboardCaptureFilters: mouseCaptureFilters mouseCaptureFilters: removeEventCaptureFilter: removeKeyboardCaptureFilter: removeMouseCaptureFilter:) ('events-filtering-bubbling' addEventBubbleFilter: addKeyboardBubbleFilter: addMouseBubbleFilter: eventBubbleFilters eventBubbleFilters: keyboardBubbleFilters keyboardBubbleFilters: mouseBubbleFilters mouseBubbleFilters: removeEventBubbleFilter: removeKeyboardBubbleFilter: removeMouseBubbleFilter:) ('events-filtering' addFilter:to: removeFilter:from:) ('events-removing' releaseActionMap) ('fileIn/out' attachToResource prepareToBeSaved reserveUrl: saveAsResource saveDocPane saveOnFile saveOnURL saveOnURLbasic saveOnURL: updateAllFromResources updateFromResource) ('filter streaming' drawOnCanvas:) ('geometry' align:with: bottom bottomCenter bottomLeft bottomLeft: bottomRight bottomRight: bottom: bounds boundsInWorld boundsIn: bounds: bounds:from: bounds:in: center center: extent extent: fullBoundsInWorld globalPointToLocal: griddedPoint: gridPoint: height height: innerBounds intersects: left leftCenter left: localPointToGlobal: minimumExtent minimumExtent: minimumHeight minimumHeight: minimumWidth minimumWidth: outerBounds overlapsShadowForm:bounds: pointFromWorld: pointInWorld: point:from: point:in: position positionInWorld positionSubmorphs position: right rightCenter right: screenLocation screenRectangle setConstrainedPosition:hangOut: shiftSubmorphsOtherThan:by: top topCenter topLeft topLeft: topRight topRight: top: transformedBy: width width: worldBounds worldBoundsForHalo) ('geometry testing' containsPoint: fullContainsPoint: obtrudesBeyondContainer) ('halos and balloon help' addHalo addHalo: addHalo:from: addHandlesTo:box: addMagicHaloFor: addOptionalHandlesTo:box: addSimpleHandlesTo:box: addWorldHandlesTo:box: balloonColor balloonColor: balloonFont balloonFont: balloonHelpAligner balloonHelpDelayTime balloonHelpTextForHandle: balloonMorphClass boundsForBalloon comeToFrontAndAddHalo createHalo defaultBalloonColor defaultBalloonFont defersHaloOnClickTo: deleteBalloon editBalloonHelpContent: editBalloonHelpText halo haloClass haloDelayTime hasHalo hasHalo: isLikelyRecipientForMouseOverHalos mouseDownOnHelpHandle: noHelpString okayToAddDismissHandle okayToAddGrabHandle okayToBrownDragEasily okayToExtractEasily okayToResizeEasily okayToRotateEasily preferredDuplicationHandleSelector removeHalo setBalloonText: setBalloonText:maxLineLength: setCenteredBalloonText: showBalloon showBalloon: showBalloon:at: showBalloon:hand: transferHalo:from: wantsBalloon wantsDirectionHandles wantsDirectionHandles: wantsHalo wantsHaloFor: wantsHaloFromClick wantsHaloHandleWithSelector:inHalo: wantsScriptorHaloHandle wantsSimpleSketchMorphHandles) ('initialization' basicInitialize defaultBounds defaultColor inAScrollPane inATwoWayScrollPane initialize intoWorld: openCenteredInWorld openInHand openInWindow openInWindowLabeled: openInWindowLabeled:inWorld: openInWorld openInWorld: openNear: openNear:in: openNearMorph: outOfWorld: resourceJustLoaded standardPalette) ('layout' acceptDroppingMorph:event: adjustLayoutBounds doLayoutIn: fullBounds layoutBounds layoutBounds: layoutChanged layoutInBounds: layoutProportionallyIn: minExtent minExtent: minHeight minHeight: minWidth minWidth: privateFullBounds submorphBounds) ('layout-menu' addCellLayoutMenuItems:hand: addLayoutMenuItems:hand: addTableLayoutMenuItems:hand: changeCellInset: changeClipLayoutCells changeDisableTableLayout changeLayoutInset: changeListDirection: changeMaxCellSize: changeMinCellSize: changeNoLayout changeProportionalLayout changeReverseCells changeRubberBandCells changeTableLayout hasClipLayoutCellsString hasDisableTableLayoutString hasNoLayoutString hasProportionalLayoutString hasReverseCellsString hasRubberBandCellsString hasTableLayoutString layoutMenuPropertyString:from:) ('layout-properties' assureLayoutProperties assureTableProperties cellInset cellInset: cellPositioning cellPositioningString: cellPositioning: cellSpacing cellSpacingString: cellSpacing: copyLayoutProperties disableTableLayout disableTableLayout: hResizing hResizingString: hResizing: layoutFrame layoutFrame: layoutInset layoutInset: layoutPolicy layoutPolicy: layoutProperties layoutProperties: listCentering listCenteringString: listCentering: listDirection listDirectionString: listDirection: listSpacing listSpacingString: listSpacing: maxCellSize maxCellSize: minCellSize minCellSize: reverseTableCells reverseTableCells: rubberBandCells rubberBandCells: spaceFillWeight spaceFillWeight: vResizeToFit: vResizing vResizingString: vResizing: wrapCentering wrapCenteringString: wrapCentering: wrapDirection wrapDirectionString: wrapDirection:) ('macpal' flash) ('menu' addBorderStyleMenuItems:hand: addGestureMenuItems:hand: addGraphModelYellowButtonItemsTo:event: addModelYellowButtonItemsTo:event: addMyYellowButtonMenuItemsToSubmorphMenus addNestedYellowButtonItemsTo:event: addTitleForHaloMenu: addYellowButtonMenuItemsTo:event: buildYellowButtonMenu: hasYellowButtonMenu offerCostumeViewerMenu: outermostOwnerWithYellowButtonMenu startWiring wantsYellowButtonMenu wantsYellowButtonMenu:) ('menus' absorbStateFromRenderer: addAddHandMenuItemsForHalo:hand: addCopyItemsTo: addCustomHaloMenuItems:hand: addCustomMenuItems:hand: addExportMenuItems:hand: addFillStyleMenuItems:hand: addHaloActionsTo: addMiscExtrasTo: addPaintingItemsTo:hand: addStandardHaloMenuItemsTo:hand: addToggleItemsToHaloMenu: addWorldTargetSightingItems:hand: adhereToEdge adhereToEdge: adjustedCenter adjustedCenter: allMenuWordings changeColor changeDirectionHandles changeDragAndDrop chooseNewGraphic chooseNewGraphicCoexisting: chooseNewGraphicFromHalo collapse defaultArrowheadSize doMenuItem: exploreInMorphic exploreInMorphic: exportAsBMP exportAsBMPNamed: exportAsGIF exportAsGIFNamed: exportAsJPEG exportAsJPEGNamed: exportAsPNG exportAsPNGNamed: hasDirectionHandlesString hasDragAndDropEnabledString helpButton inspectInMorphic inspectInMorphic: lockedString lockUnlockMorph makeNascentScript maybeAddCollapseItemTo: menuItemAfter: menuItemBefore: model presentHelp reasonableBitmapFillForms reasonableForms resetForwardDirection resistsRemovalString setArrowheads setRotationCenter setRotationCenterFrom: setToAdhereToEdge: snapToEdgeIfAppropriate stickinessString transferStateToRenderer: uncollapseSketch) ('meta-actions' addEmbeddingMenuItemsTo:hand: applyStatusToAllSiblings: beThisWorldsModel blueButtonDown: blueButtonUp: bringAllSiblingsToMe: buildHandleMenu: buildMetaMenu: changeColorTarget:selector:originalColor:hand: copyToPasteBuffer: dismissMorph dismissMorph: duplicateMorphImage: duplicateMorph: embedInto: grabMorph: handlerForBlueButtonDown: handlerForMetaMenu: indicateAllSiblings inspectAt:event: invokeMetaMenuAt:event: invokeMetaMenu: makeMultipleSiblings: makeNewPlayerInstance: makeSiblingsLookLikeMe: makeSiblings: maybeDuplicateMorph maybeDuplicateMorph: openAPropertySheet openATextPropertySheet potentialEmbeddingTargets potentialTargets potentialTargetsAt: resizeFromMenu resizeMorph: saveAsPrototype showActions showHiders sightTargets: sightWorldTargets: subclassMorph targetFromMenu: targetWith:) ('miscellaneous' roundUpStrays setExtentFromHalo: setFlexExtentFromHalo:) ('naming' downshiftedNameOfObjectRepresented innocuousName nameForFindWindowFeature nameInModel nameOfObjectRepresented name: setNamePropertyTo: setNameTo: specialNameInModel tryToRenameTo: updateAllScriptingElements) ('objects from disk' objectForDataStream: storeDataOn:) ('other events' menuButtonMouseEnter: menuButtonMouseLeave:) ('parts bin' initializeToStandAlone inPartsBin isPartsBin isPartsDonor isPartsDonor: markAsPartsDonor partRepresented residesInPartsBin) ('printing' clipText colorString: constructorString fullPrintOn: initString morphReport morphReportFor: morphReportFor:on:indent: pagesHandledAutomatically printConstructorOn:indent: printConstructorOn:indent:nodeDict: printOn: printSpecs printSpecs: printStructureOn:indent: reportableSize structureString textToPaste) ('rotate scale and flex' addFlexShell addFlexShellIfNecessary keepsTransform newTransformationMorph removeFlexShell rotationDegrees) ('rounding' cornerRadius cornerRadius: cornerStyle cornerStyle: roundedCorners roundedCornersString toggleCornerRounding wantsRoundedCorners) ('stepping and presenter' arrangeToStartStepping arrangeToStartSteppingIn: isStepping isSteppingSelector: start startStepping startSteppingIn: startSteppingSelector: startStepping:at:arguments:stepTime: step stepAt: stop stopStepping stopSteppingSelector: stopSteppingSelfAndSubmorphs) ('structure' activeHand allOwners allOwnersDo: containingWindow firstOwnerSuchThat: hasOwner: isInDockingBar isInSystemWindow isInWorld morphPreceding: nearestOwnerThat: orOwnerSuchThat: outermostMorphThat: outermostWorldMorph owner ownerThatIsA: ownerThatIsA:orA: pasteUpMorph pasteUpMorphHandlingTabAmongFields primaryHand renderedMorph root rootAt: topPasteUp topRendererOrSelf withAllOwners withAllOwnersDo: world) ('submorphs-accessing' allKnownNames allMorphs allMorphsDo: allNonSubmorphMorphs allSubmorphNamesDo: dockingBars findA: findDeeplyA: findDeepSubmorphThat:ifAbsent: findSubmorphBinary: firstSubmorph hasSubmorphs hasSubmorphWithProperty: indexOfMorphAbove: lastSubmorph mainDockingBars morphsAt: morphsAt:behind:unlocked: morphsAt:unlocked: morphsAt:unlocked:do: morphsInFrontOf:overlapping:do: morphsInFrontOverlapping: morphsInFrontOverlapping:do: noteNewOwner: rootMorphsAtGlobal: rootMorphsAt: shuffleSubmorphs submorphAfter submorphBefore submorphCount submorphNamed: submorphNamed:ifNone: submorphOfClass: submorphs submorphsBehind:do: submorphsDo: submorphsInFrontOf:do: submorphsReverseDo: submorphsSatisfying: submorphThat:ifNone: submorphWithProperty:) ('submorphs-add/remove' abandon actWhen actWhen: addAllMorphs: addAllMorphs:after: addAllMorphs:behind: addAllMorphs:inFrontOf: addAllMorphsBack: addAllMorphsFront: addMorphBack: addMorphCentered: addMorphFrontFromWorldPosition: addMorphFront: addMorphFront:fromWorldPosition: addMorphNearBack: addMorph: addMorph:after: addMorph:asElementNumber: addMorph:behind: addMorph:fullFrame: addMorph:inFrontOf: allMorphsWithPlayersDo: comeToFront copyWithoutSubmorph: delete deleteDockingBars deleteSubmorphsWithProperty: deleteUnlessHasFocus dismissViaHalo goBehind privateDelete removeAllMorphs removeAllMorphsIn: removedMorph: removeMorph: replaceSubmorph:by: submorphIndexOf:) ('testing' canDrawAtHigherResolution canDrawBorder: completeModificationHash couldMakeSibling indicateKeyboardFocus isDockingBar isFlexed isFullOnScreen isImageMorph isLineMorph isMenuItemMorph isMorph isSafeToServe isSelectionMorph isSketchMorph knownName modificationHash renameInternal: renameTo: shouldDropOnMouseUp stepTime wantsSteps) ('text-anchor' addTextAnchorMenuItems:hand: changeDocumentAnchor changeInlineAnchor changeParagraphAnchor hasDocumentAnchorString hasInlineAnchorString hasParagraphAnchorString relativeTextAnchorPosition relativeTextAnchorPosition: textAnchorType textAnchorType:) ('thumbnail' demandsThumbnailing icon iconOrThumbnail iconOrThumbnailOfSize: morphRepresented permitsThumbnailing readoutForField: representativeNoTallerThan:norWiderThan:thumbnailHeight: thumbnail updateThumbnailUrl updateThumbnailUrlInBook:) ('undo' commandHistory undoMove:redo:owner:bounds:predecessor:) ('updating' changed) ('user interface' becomeModal defaultLabelForInspector doCancel initialExtent) ('viewer' externalName) ('visual properties' canHaveFillStyles defaultBitmapFillForm fillStyle fillStyle: fillWithRamp:oriented: useBitmapFill useDefaultFill useGradientFill useSolidFill) ('WiW support' addMorphInFrontOfLayer: addMorphInLayer: eToyRejectDropMorph:event: morphicLayerNumber morphicLayerNumberWithin: randomBoundsFor: shouldGetStepsFrom:) ('*MorphicExtras-accessing' highlightOnlySubmorph:) ('*MorphicExtras-geometry' shiftSubmorphsBy:) ('*MorphicExtras-menus' dismissButton printPSToFileNamed:) ('*services-base' requestor) ('private' canBeEncroached privateAddAllMorphs:atIndex: privateAddMorph:atIndex: privateBounds: privateColor: privateDeleteWithAbsolutelyNoSideEffects privateFullBounds: privateFullMoveBy: privateMoveBy: privateOwner: privateRemoveMorphWithAbsolutelyNoSideEffects: privateRemove: privateSubmorphs privateSubmorphs:) ('accessing-backstop' target:) ('e-toy support' adaptToWorld: allMorphsAndBookPagesInto: asNumber: automaticViewing currentPlayerDo: cursor cursor: decimalPlacesForGetter: defaultValueOrNil embeddedInMorphicWindowLabeled: embedInWindow getNumericValue gridFormOrigin:grid:background:line: handUserASibling isAViewer isTileEditor makeGraphPaper makeGraphPaperGrid:background:line: mustBeBackmost noteDecimalPlaces:forGetter: objectViewed referencePlayfield rotationStyle rotationStyle: setAsActionInButtonProperties: setNumericValue: setStandardTexture textureParameters unlockOneSubpart updateCachedThumbnail wantsRecolorHandle wrappedInWindowWithTitle: wrappedInWindow:) ('geniestubs' allowsGestureStart: isGestureStart: mouseStillDownStepRate redButtonGestureDictionaryOrName: yellowButtonGestureDictionaryOrName:) ('*morphic-Postscript Canvases' asPostscript clipPostscript drawPostscriptOn: fullDrawPostscriptOn: printPSToFile) ('player' assureExternalName okayToDuplicate shouldRememberCostumes) ('player commands' playSoundNamed:) ('button' doButtonAction) ('model access' models) ('player viewer' openViewerForArgument) ('other' removeAllButFirstSubmorph) ('selected object' selectedObject) ('polymorph' modalLockTo: modalUnlockFrom: openModal:) ('*Etoys-card in a stack' abstractAModel addStackItemsTo: assuredCardPlayer beAStackBackground becomeSharedBackgroundField containsCard: couldHoldSeparateDataForEachInstance currentDataInstance currentDataValue explainDesignations goToNextCardInStack goToPreviousCardInStack holdsSeparateDataForEachInstance insertAsStackBackground insertCard installAsCurrent: isStackBackground makeHoldSeparateDataForEachInstance newCard reassessBackgroundShape relaxGripOnVariableNames reshapeBackground setAsDefaultValueForNewCard showBackgroundObjects showDesignationsOfObjects showForegroundObjects stack stackDo: stopHoldingSeparateDataForEachInstance tabHitWithEvent: variableDocks wrapWithAStack) ('*Etoys' accumlatePlayersInto:andSelectorsInto: actorState addPlayerItemsTo: assuredPlayer categoriesForViewer choosePenColor: choosePenSize currentVocabulary ensuredButtonProperties getPenColor getPenDown getPenSize isPlayer:ofReferencingTile: isTurtleRow liftPen lowerPen moveWithPenDownBy: newPlayerInstance penColor: penUpWhile: putOnBackground putOnForeground scriptPerformer selectorsForViewer selectorsForViewerIn: showPlayerMenu trailMorph traverseRowTranslateSlotOld:of:to: traverseRowTranslateSlotOld:to: understandsBorderVocabulary unfilteredCategoriesForViewer) ('*Etoys-support' adoptVocabulary: affiliatedSelector appearsToBeSameCostumeAs: asWearableCostume asWearableCostumeOfExtent: beep: buttonProperties buttonProperties: changeAllBorderColorsFrom:to: configureForKids copyCostumeStateFrom: creationStamp defaultVariableName definePath deletePath enclosingEditor enforceTileColorPolicy fenceEnabled fire firedMouseUpCode followPath getCharacters handMeTilesToFire hasButtonProperties isCandidateForAutomaticViewing jumpTo: listViewLineForFieldList: makeFenceSound noteNegotiatedName:for: pinkXButton set: slotSpecifications succeededInRevealing: tanOButton topEditor updateLiteralLabel) ('*Etoys-scripting' arrowDeltaFor: asEmptyPermanentScriptor bringTileScriptingElementsUpToDate bringUpToDate defaultFloatPrecisionFor: filterViewerCategoryDictionary: isTileLike isTileScriptingElement jettisonScripts makeAllTilesColored makeAllTilesGreen restoreTypeColor scriptEditorFor: tearOffTile triggerScript: useUniformTileColor viewAfreshIn:showingScript:at: wantsConnectionVocabulary) ('*Etoys-customevents-scripting' instantiatedUserScriptsDo: removeAllEventTriggers removeAllEventTriggersFor: removeEventTrigger: removeEventTrigger:for: renameScriptActionsFor:from:to: triggerCustomEvent: triggerEtoyEvent: triggerEtoyEvent:from:) ('*Etoys-geometry' addTransparentSpacerOfSize: beTransparent cartesianBoundsTopLeft cartesianXY: color:sees: colorUnder degreesOfFlex forwardDirection: getIndexInOwner goHome heading heading: move:toPosition: referencePosition referencePosition: referencePositionInWorld referencePositionInWorld: rotationCenter rotationCenter: scale: scaleFactor: setDirectionFrom: setIndexInOwner: simplySetVisible: touchesColor: transparentSpacerOfSize: wrap x x: x:y: y y:) ('*Etoys-latter day support' isEtoyReadout) ('*MorphicExtras-postscript' asEPS asPostscriptPrintJob) ('*Morphic-Sound-piano rolls' addMorphsTo:pianoRoll:eventTime:betweenTime:and: encounteredAtTime:inScorePlayer:atIndex:inEventTrack:secsPerTick: justDroppedIntoPianoRoll:event: pauseFrom: resetFrom: resumeFrom: triggerActionFromPianoRoll) ('*MorphicExtras' nextOwnerPage previousOwnerPage) !