### Eclipse Workspace Patch 1.0 #P org.eclipse.swt Index: Eclipse SWT/cocoa/org/eclipse/swt/widgets/Composite.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Composite.java,v retrieving revision 1.93 diff -u -r1.93 Composite.java --- Eclipse SWT/cocoa/org/eclipse/swt/widgets/Composite.java 22 Dec 2010 16:04:34 -0000 1.93 +++ Eclipse SWT/cocoa/org/eclipse/swt/widgets/Composite.java 13 Jan 2011 17:43:20 -0000 @@ -941,48 +941,54 @@ getShell().deferFlushing(); NSEvent nsEvent = new NSEvent(theEvent); boolean handled = false; - float /*double*/ delta = nsEvent.deltaY(); - if (display.gestureStarted) { - if (!sendGestureEvent(nsEvent, SWT.GESTURE_PAN, true)) handled = true; - } - if (delta != 0) { - boolean doit = true; - if (hooks (SWT.MouseWheel) || filters (SWT.MouseWheel)) { - doit = sendMouseEvent(nsEvent, SWT.MouseWheel, true); - } - ScrollBar bar = verticalBar; - if (doit && bar != null && bar.getEnabled ()) { - if (-1 < delta && delta < 0) delta = -1; - if (0 < delta && delta < 1) delta = 1; - int selection = Math.max (0, (int)(0.5f + bar.getSelection () - bar.getIncrement () * delta)); - bar.setSelection (selection); - Event event = new Event (); - event.detail = delta > 0 ? SWT.PAGE_UP : SWT.PAGE_DOWN; - bar.sendSelectionEvent (SWT.Selection, event, true); - handled = true; + float /*double*/ deltaY = nsEvent.deltaY(); + float /*double*/ deltaX = nsEvent.deltaX (); + if ((hooks(SWT.Gesture) || filters (SWT.Gesture))) { + if (deltaX != 0 || deltaY != 0) { + if (!gestureEvent(id, theEvent, SWT.GESTURE_PAN)) { + handled = true; + } } - if (!doit) handled = true; } - delta = nsEvent.deltaX (); - if (delta != 0) { - boolean doit = true; - if (hooks (SWT.MouseHorizontalWheel) || filters (SWT.MouseHorizontalWheel)) { - doit = sendMouseEvent(nsEvent, SWT.MouseHorizontalWheel, true); + if (!handled) { + if (deltaY != 0) { + boolean doit = true; + if (hooks (SWT.MouseWheel) || filters (SWT.MouseWheel)) { + doit = sendMouseEvent(nsEvent, SWT.MouseWheel, true); + } + ScrollBar bar = verticalBar; + if (doit && bar != null && bar.getEnabled ()) { + if (-1 < deltaY && deltaY < 0) deltaY = -1; + if (0 < deltaY && deltaY < 1) deltaY = 1; + int selection = Math.max (0, (int)(0.5f + bar.getSelection () - bar.getIncrement () * deltaY)); + bar.setSelection (selection); + Event event = new Event (); + event.detail = deltaY > 0 ? SWT.PAGE_UP : SWT.PAGE_DOWN; + bar.sendSelectionEvent (SWT.Selection, event, true); + handled = true; + } + if (!doit) handled = true; } - ScrollBar bar = horizontalBar; - if (doit && bar != null && bar.getEnabled ()) { - if (-1 < delta && delta < 0) delta = -1; - if (0 < delta && delta < 1) delta = 1; - int selection = Math.max (0, (int)(0.5f + bar.getSelection () - bar.getIncrement () * delta)); - bar.setSelection (selection); - Event event = new Event (); - event.detail = delta > 0 ? SWT.PAGE_UP : SWT.PAGE_DOWN; - bar.sendSelectionEvent (SWT.Selection, event, true); - handled = true; + if (deltaX != 0) { + boolean doit = true; + if (hooks (SWT.MouseHorizontalWheel) || filters (SWT.MouseHorizontalWheel)) { + doit = sendMouseEvent(nsEvent, SWT.MouseHorizontalWheel, true); + } + ScrollBar bar = horizontalBar; + if (doit && bar != null && bar.getEnabled ()) { + if (-1 < deltaX && deltaX < 0) deltaX = -1; + if (0 < deltaX && deltaX < 1) deltaX = 1; + int selection = Math.max (0, (int)(0.5f + bar.getSelection () - bar.getIncrement () * deltaX)); + bar.setSelection (selection); + Event event = new Event (); + event.detail = deltaX > 0 ? SWT.PAGE_UP : SWT.PAGE_DOWN; + bar.sendSelectionEvent (SWT.Selection, event, true); + handled = true; + } + if (!doit) handled = true; } - if (!doit) handled = true; + if (!handled) view.superview().scrollWheel(nsEvent); } - if (!handled) view.superview().scrollWheel(nsEvent); return; } callSuper(id, sel, theEvent); Index: Eclipse SWT/cocoa/org/eclipse/swt/widgets/Control.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Control.java,v retrieving revision 1.215 diff -u -r1.215 Control.java --- Eclipse SWT/cocoa/org/eclipse/swt/widgets/Control.java 23 Dec 2010 19:24:18 -0000 1.215 +++ Eclipse SWT/cocoa/org/eclipse/swt/widgets/Control.java 13 Jan 2011 17:43:21 -0000 @@ -68,7 +68,8 @@ NSBezierPath regionPath; int /*long*/ visibleRgn; Accessible accessible; - + boolean touchEnabled; + final static int CLIPPING = 1 << 10; final static int VISIBLE_REGION = 1 << 12; @@ -657,6 +658,35 @@ /** * Adds the listener to the collection of listeners who will + * be notified when touch events occur, by sending it + * one of the messages defined in the TouchListener + * interface. + *

+ * NOTE: You must also call setTouchEventsEnabled to notify the + * windowing toolkit that you want touch events to be generated. + *

+ * + * @exception IllegalArgumentException + * @exception SWTException + * + * @see TouchListener + * @see #removeTouchListener + * @since 3.7 + */ +public void addTouchListener (TouchListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.Touch,typedListener); +} + +/** + * Adds the listener to the collection of listeners who will * be notified when traversal events occur, by sending it * one of the messages defined in the TraverseListener * interface. @@ -687,12 +717,12 @@ } void beginGestureWithEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ event) { - if (!gestureEvent(id, sel, event, SWT.GESTURE_BEGIN)) return; + if (!gestureEvent(id, event, SWT.GESTURE_BEGIN)) return; super.beginGestureWithEvent(id, sel, event); } void endGestureWithEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ event) { - if (!gestureEvent(id, sel, event, SWT.GESTURE_END)) return; + if (!gestureEvent(id, event, SWT.GESTURE_END)) return; super.endGestureWithEvent(id, sel, event); } @@ -1389,15 +1419,59 @@ return view.window ().makeFirstResponder (focusView); } -boolean gestureEvent(int /*long*/ id, int /*long*/ sel, int /*long*/ event, int detail) { +boolean gestureEvent(int /*long*/ id, int /*long*/ eventPtr, int detail) { // For cross-platform compatibility, touch events and gestures are mutually exclusive. // Don't send a gesture if touch events are enabled for this control. -// if (touchEnabled) return true; - if (!display.sendEvent) return true; - display.sendEvent = false; + if (touchEnabled) return true; if (!isEventView (id)) return true; - NSEvent nsEvent = new NSEvent(event); - return sendGestureEvent (nsEvent, detail, true); + if (!hooks(SWT.Gesture) && !filters(SWT.Gesture)) return true; + NSEvent nsEvent = new NSEvent(eventPtr); + Event event = new Event(); + NSPoint windowPoint; + NSView view = eventView (); + windowPoint = nsEvent.locationInWindow(); + NSPoint point = view.convertPoint_fromView_(windowPoint, null); + if (!view.isFlipped ()) { + point.y = view.bounds().height - point.y; + } + event.x = (int) point.x; + event.y = (int) point.y; + setInputState (event, nsEvent, SWT.Gesture); + event.detail = detail; + + if (detail == SWT.GESTURE_BEGIN) { + display.gestureStarted = true; + display.rotation = 0.0; + display.magnification = 1.0; + } else if (detail == SWT.GESTURE_END) { + display.gestureStarted = false; + } + + switch (detail) { + case SWT.GESTURE_SWIPE: + event.xDirection = (int) -nsEvent.deltaX(); + event.yDirection = (int) -nsEvent.deltaY(); + break; + case SWT.GESTURE_ROTATE: { + display.rotation += nsEvent.rotation(); + event.rotation = display.rotation; + break; + } + case SWT.GESTURE_MAGNIFY: + display.magnification += nsEvent.magnification(); + event.magnification = display.magnification; + break; + case SWT.GESTURE_PAN: + // Panning increment is expressed in terms of the direction of movement, + // not in terms of scrolling increment. + event.xDirection = (int) -nsEvent.deltaX(); + event.yDirection = (int) -nsEvent.deltaY(); + if (event.xDirection == 0 && event.yDirection == 0) return false; + break; + } + + sendEvent (SWT.Gesture, event); + return event.doit; } /** @@ -2248,7 +2322,7 @@ } void magnifyWithEvent(int /*long*/ id, int /*long*/ sel, int /*long*/ event) { - if (!gestureEvent(id, sel, event, SWT.GESTURE_MAGNIFY)) return; + if (!gestureEvent(id, event, SWT.GESTURE_MAGNIFY)) return; super.magnifyWithEvent(id, sel, event); } @@ -2289,22 +2363,26 @@ boolean handled = false; if (id == view.id) { NSEvent nsEvent = new NSEvent(theEvent); - if (display.gestureStarted && hooks(SWT.Gesture)) { - if (!sendGestureEvent(nsEvent, SWT.GESTURE_PAN, true)) { - handled = true; + if ((hooks(SWT.Gesture) || filters(SWT.Gesture))) { + if (nsEvent.deltaY() != 0 || nsEvent.deltaX() != 0) { + if (!gestureEvent(id, theEvent, SWT.GESTURE_PAN)) { + handled = true; + } } } - if (hooks (SWT.MouseWheel) || filters (SWT.MouseWheel)) { - if (nsEvent.deltaY() != 0) { - if (!sendMouseEvent(nsEvent, SWT.MouseWheel, true)) { - handled = true; + if (!handled) { + if (hooks (SWT.MouseWheel) || filters (SWT.MouseWheel)) { + if (nsEvent.deltaY() != 0) { + if (!sendMouseEvent(nsEvent, SWT.MouseWheel, true)) { + handled = true; + } } } - } - if (hooks (SWT.MouseHorizontalWheel) || filters (SWT.MouseHorizontalWheel)) { - if (nsEvent.deltaX() != 0) { - if (!sendMouseEvent(nsEvent, SWT.MouseHorizontalWheel, true)) { - handled = true; + if (hooks (SWT.MouseHorizontalWheel) || filters (SWT.MouseHorizontalWheel)) { + if (nsEvent.deltaX() != 0) { + if (!sendMouseEvent(nsEvent, SWT.MouseHorizontalWheel, true)) { + handled = true; + } } } } @@ -3034,6 +3112,31 @@ /** * Removes the listener from the collection of listeners who will + * be notified when touch events occur. + * + * @param listener the listener which should no longer be notified + * + * @exception IllegalArgumentException + * @exception SWTException + * + * @see TouchListener + * @see #addTouchListener + * @since 3.7 + */ +public void removeTouchListener(TouchListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.Touch, listener); +} + +/** + * Removes the listener from the collection of listeners who will * be notified when traversal events occur. * * @param listener the listener which should no longer be notified @@ -3083,7 +3186,7 @@ } void rotateWithEvent(int /*long*/ id, int /*long*/ sel, int /*long*/ event) { - if (!gestureEvent(id, sel, event, SWT.GESTURE_ROTATE)) return; + if (!gestureEvent(id, event, SWT.GESTURE_ROTATE)) return; super.rotateWithEvent(id, sel, event); } @@ -3128,56 +3231,6 @@ } } -boolean sendGestureEvent (NSEvent nsEvent, int detail, boolean send) { - Event event = new Event (); - NSPoint windowPoint; - NSView view = eventView (); - windowPoint = nsEvent.locationInWindow(); - NSPoint point = view.convertPoint_fromView_(windowPoint, null); - if (!view.isFlipped ()) { - point.y = view.bounds().height - point.y; - } - event.x = (int) point.x; - event.y = (int) point.y; - setInputState (event, nsEvent, SWT.Gesture); - event.detail = detail; - - if (detail == SWT.GESTURE_BEGIN) { - display.gestureStarted = true; - display.rotation = 0.0; - display.magnification = 1.0; - } else if (detail == SWT.GESTURE_END) { - display.gestureStarted = false; - } - - switch (detail) { - case SWT.GESTURE_SWIPE: - event.xDirection = (int) -nsEvent.deltaX(); - event.yDirection = (int) -nsEvent.deltaY(); - break; - case SWT.GESTURE_ROTATE: { - display.rotation += nsEvent.rotation(); - event.rotation = display.rotation; - break; - } - case SWT.GESTURE_MAGNIFY: - display.magnification += nsEvent.magnification(); - event.magnification = display.magnification; - break; - case SWT.GESTURE_PAN: - // Panning increment is expressed in terms of the direction of movement, - // not in terms of scrolling increment. - event.xDirection = (int) -nsEvent.deltaX(); - event.yDirection = (int) -nsEvent.deltaY(); - break; - } - - event.doit = true; - sendEvent (SWT.Gesture, event); - if (isDisposed ()) return false; - return event.doit; -} - boolean sendMouseEvent (NSEvent nsEvent, int type, boolean send) { Shell shell = null; Event event = new Event (); @@ -3235,6 +3288,49 @@ return event.doit; } +Touch touchStateFromNSTouch(NSTouch touch) { + TouchSource source = display.findTouchSource(touch); + int /*long*/ osPhase = touch.phase(); + long identity = OS.objc_msgSend(touch.id, OS.sel_identity); + int state = 0; + + switch ((int)osPhase) { + case OS.NSTouchPhaseBegan: + state = SWT.TOUCHSTATE_DOWN; + break; + case OS.NSTouchPhaseMoved: + state = SWT.TOUCHSTATE_MOVE; + break; + case OS.NSTouchPhaseEnded: + case OS.NSTouchPhaseCancelled: + state = SWT.TOUCHSTATE_UP; + break; + } + + display.touchCounter++; + boolean primary = false; + NSPoint normalizedPos = touch.normalizedPosition(); + double normalizedX = normalizedPos.x; + double normalizedY = 1 - normalizedPos.y; + if (display.currentTouches().count() == 1) display.primaryIdentifier = identity; + if (display.primaryIdentifier == identity) primary = true; + NSSize deviceSize = touch.deviceSize(); + int screenX = (int) (normalizedX * deviceSize.width); + int screenY = (int) (normalizedY * deviceSize.height); + Touch newTS = new Touch(identity, source, state, primary, screenX, screenY); + return newTS; +} + +NSTouch findTouchWithId(NSArray touches, NSObject identity) { + int /*long*/ count = touches.count(); + for (int /*long*/ i = 0; i < count; i++) { + NSTouch aTouch = new NSTouch(touches.objectAtIndex(i).id); + NSObject currIdentity = new NSObject(OS.objc_msgSend(aTouch.id, OS.sel_identity)); + if (currIdentity.isEqual(identity)) return aTouch; + } + return null; +} + void setBackground () { if (!drawsBackground()) return; Control control = findBackgroundControl (); @@ -3928,6 +4024,27 @@ } /** + * Sets the receiver to receive touch events from the OS. By default, touch + * events are not delivered to a control unless specifically requested for that control. + * This is independent of whether or not there are any TouchListener instances + * registered for the control. + * + * @param enabled the new touch-enabled state. + * + * @exception SWTException + * + * @since 3.7 + */ +public void setTouchEventsEnabled(boolean enabled) { + checkWidget(); + eventView().setAcceptsTouchEvents(enabled); + touchEnabled = enabled; +} + +/** * Marks the receiver as visible if the argument is true, * and marks it invisible otherwise. *

@@ -4092,7 +4209,7 @@ } void swipeWithEvent(int /*long*/ id, int /*long*/ sel, int /*long*/ event) { - if (!gestureEvent(id, sel, event, SWT.GESTURE_SWIPE)) return; + if (!gestureEvent(id, event, SWT.GESTURE_SWIPE)) return; super.swipeWithEvent(id, sel, event); } @@ -4197,6 +4314,94 @@ return view; } +void touchEvent(int /*long*/ id, int /*long*/ sel, int /*long*/ eventPtr) { + if (!(hooks(SWT.Touch) || filters(SWT.Touch))) return; + if (!isEventView (id)) return; + NSEvent nsEvent = new NSEvent(eventPtr); + NSMutableArray currentTouches = display.currentTouches(); + Event event = new Event (); + NSPoint location = view.convertPoint_fromView_(nsEvent.locationInWindow(), null); + if (!view.isFlipped ()) { + location.y = view.bounds().height - location.y; + } + event.x = (int) location.x; + event.y = (int) location.y; + setInputState (event, nsEvent, SWT.Touch); + NSSet allTouchesSet = nsEvent.touchesMatchingPhase(OS.NSTouchPhaseAny, null); + int /*long*/ touchCount = allTouchesSet.count(); + Touch touches[] = new Touch[(int)/*64*/touchCount]; + int currTouchIndex = 0; + + // Process removed/cancelled touches first. + NSArray endedTouches = nsEvent.touchesMatchingPhase(OS.NSTouchPhaseEnded | OS.NSTouchPhaseCancelled, null).allObjects(); + + for (int i = 0; i < endedTouches.count(); i++) { + NSTouch touch = new NSTouch(endedTouches.objectAtIndex(i).id); + NSObject identity = new NSObject(OS.objc_msgSend(touch.id, OS.sel_identity)); + NSTouch endedTouch = findTouchWithId(currentTouches, identity); + if (endedTouch != null) currentTouches.removeObject(endedTouch); + touches[currTouchIndex++] = touchStateFromNSTouch(touch); + } + + if (currentTouches.count() == 0) display.touchCounter = 0; + + // Process touches in progress or starting. + NSArray activeTouches = nsEvent.touchesMatchingPhase(OS.NSTouchPhaseBegan | OS.NSTouchPhaseMoved | OS.NSTouchPhaseStationary, null).allObjects(); + + for (int i = 0; i < activeTouches.count(); i++) { + NSTouch touch = new NSTouch(activeTouches.objectAtIndex(i).id); + NSObject identity = new NSObject(OS.objc_msgSend(touch.id, OS.sel_identity)); + NSTouch activeTouch = findTouchWithId(currentTouches, identity); + if (activeTouch == null) currentTouches.addObject(touch); + touches[currTouchIndex++] = touchStateFromNSTouch(touch); + } + +// if (activeTouches.count() != currentTouches.count()) { +// /** +// * Bug in Cocoa. Under some situations we don't get the NSTouchPhaseEnded/Cancelled notification. Most commonly this happens +// * if a 4-finger gesture occurs and the application switcher appears. Workaround is to generate a TOUCHSTATE_UP for the +// * orphaned touch. +// */ +// for (int /*long*/ j = currentTouches.count() - 1; j >= 0 ; j--) { +// NSTouch touch = new NSTouch(currentTouches.objectAtIndex(j).id); +// NSObject identity = new NSObject(OS.objc_msgSend(touch.id, OS.sel_identity)); +// NSTouch activeTouch = findTouchWithId(activeTouches, identity); +// if (activeTouch == null) { +// Touch fakeTouchUp = touchStateFromNSTouch(touch); +// fakeTouchUp.phase = Touch.TOUCHSTATE_UP; +// +// if (currTouchIndex == touches.length) { +// Touch newTouchStates[] = new Touch[touches.length + 1]; +// System.arraycopy(touches, 0, newTouchStates, 0, touches.length); +// touches = newTouchStates; +// } +// +// touches[currTouchIndex++] = fakeTouchUp; +// currentTouches.removeObject(activeTouch); +// } +// } +// } + + event.touches = touches; + postEvent (SWT.Touch, event); +} + +void touchesBeganWithEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ event) { + touchEvent(id, sel, event); +} + +void touchesCancelledWithEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ event) { + touchEvent(id, sel, event); +} + +void touchesEndedWithEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ event) { + touchEvent(id, sel, event); +} + +void touchesMovedWithEvent (int /*long*/ id, int /*long*/ sel, int /*long*/ event) { + touchEvent(id, sel, event); +} + boolean translateTraversal (int key, NSEvent theEvent, boolean [] consume) { int detail = SWT.TRAVERSE_NONE; int code = traversalCode (key, theEvent); Index: Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java,v retrieving revision 1.366 diff -u -r1.366 Display.java --- Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java 11 Jan 2011 19:19:17 -0000 1.366 +++ Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java 13 Jan 2011 17:43:23 -0000 @@ -111,6 +111,17 @@ double magnification; boolean gestureStarted; + /* touch event state */ + int touchCounter; + long primaryIdentifier; + private NSMutableArray currentTouches; + private Map touchSourceMap; + + NSPoint referenceMouseLoc; + double refTrackpadX; + double refTrackpadY; + + /* Key event management */ int [] deadKeyState = new int[1]; int currentKeyboardUCHRdata; @@ -985,6 +996,16 @@ mainMenu.release(); } +NSMutableArray currentTouches() { + synchronized (Device.class) { + if (currentTouches == null) { + currentTouches = (NSMutableArray) new NSMutableArray().alloc(); + currentTouches = currentTouches.initWithCapacity(5); + } + } + return currentTouches; +} + int /*long*/ cursorSetProc (int /*long*/ id, int /*long*/ sel) { if (lockCursor) { if (currentControl != null) { @@ -1164,6 +1185,23 @@ } } +TouchSource findTouchSource(NSTouch touch) { + if (touchSourceMap == null) { + touchSourceMap = new HashMap(); + } + id touchDevice = touch.device(); + Long touchDeviceObj = new Long(touchDevice.id); + TouchSource returnVal = (TouchSource) touchSourceMap.get(touchDeviceObj); + + if (returnVal == null) { + Rectangle bounds = new Rectangle(0, 0, (int)Math.ceil(touch.deviceSize().width), (int)Math.ceil(touch.deviceSize().height)); + returnVal = new TouchSource(false, bounds);; + touchSourceMap.put(touchDeviceObj, returnVal); + } + + return returnVal; +} + /** * Returns the currently active Shell, or null * if no shell belonging to the currently running application @@ -2918,6 +2956,17 @@ return false; } +/** + * Returns true if a touch-aware input device is attached to the system, + * enabled, and ready for use. + * + * @since 3.7 + */ +public boolean isTouchEnabled() { + // Gestures are available on OS X 10.5.3 and later. Touch events are only available on 10.6 and later. + return (OS.VERSION > 0x1053); +} + static boolean isValidClass (Class clazz) { String name = clazz.getName (); int index = name.lastIndexOf ('.');