diff options
9 files changed, 1800 insertions, 1496 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java index 78990bf1c8..5389ac5272 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java @@ -1856,7 +1856,7 @@ int /*long*/ gtk_key_press_event (int /*long*/ widget, int /*long*/ event) { if (translateTraversal (gdkEvent)) return 1; // widget could be disposed at this point if (isDisposed ()) return 0; - return sendKeyEvent (SWT.KeyDown, gdkEvent) ? 0 : 1; + return super.gtk_key_press_event (widget, event); } int /*long*/ gtk_key_release_event (int /*long*/ widget, int /*long*/ event) { @@ -1865,9 +1865,7 @@ int /*long*/ gtk_key_release_event (int /*long*/ widget, int /*long*/ event) { if (imHandle != 0) { if (OS.gtk_im_context_filter_keypress (imHandle, event)) return 1; } - GdkEventKey gdkEvent = new GdkEventKey (); - OS.memmove (gdkEvent, event, GdkEventKey.sizeof); - return sendKeyEvent (SWT.KeyUp, gdkEvent) ? 0 : 1; + return super.gtk_key_release_event (widget, event); } int /*long*/ gtk_leave_notify_event (int /*long*/ widget, int /*long*/ event) { @@ -2309,86 +2307,6 @@ boolean sendHelpEvent (int /*long*/ helpType) { return false; } -char [] sendIMKeyEvent (int type, GdkEventKey keyEvent, char [] chars) { - int index = 0, count = 0, state = 0, time = 0; - if (keyEvent == null) { - int /*long*/ ptr = OS.gtk_get_current_event (); - if (ptr != 0) { - keyEvent = new GdkEventKey (); - OS.memmove (keyEvent, ptr, GdkEventKey.sizeof); - OS.gdk_event_free (ptr); - switch (keyEvent.type) { - case OS.GDK_KEY_PRESS: - case OS.GDK_KEY_RELEASE: - state = keyEvent.state; - time = keyEvent.time; - break; - default: - keyEvent = null; - break; - } - } - } - if (keyEvent == null) { - int [] buffer = new int [1]; - OS.gtk_get_current_event_state (buffer); - state = buffer [0]; - time = OS.gtk_get_current_event_time(); - } - while (index < chars.length) { - Event event = new Event (); - event.time = time; - if (keyEvent != null && keyEvent.length <= 1) { - setKeyState (event, keyEvent); - } else { - setInputState (event, state); - } - event.character = chars [index]; - sendEvent (type, event); - - /* - * It is possible (but unlikely), that application - * code could have disposed the widget in the key - * events. If this happens, end the processing of - * the key by returning null. - */ - if (isDisposed ()) return null; - if (event.doit) chars [count++] = chars [index]; - index++; - } - if (count == 0) return null; - if (index != count) { - char [] result = new char [count]; - System.arraycopy (chars, 0, result, 0, count); - return result; - } - return chars; -} - -boolean sendKeyEvent (int type, GdkEventKey keyEvent) { - int length = keyEvent.length; - if (length <= 1) { - Event event = new Event (); - event.time = keyEvent.time; - if (!setKeyState (event, keyEvent)) return true; - sendEvent (type, event); - // widget could be disposed at this point - - /* - * It is possible (but unlikely), that application - * code could have disposed the widget in the key - * events. If this happens, end the processing of - * the key by returning false. - */ - if (isDisposed ()) return false; - return event.doit; - } - byte [] buffer = new byte [length]; - OS.memmove (buffer, keyEvent.string, length); - char [] chars = Converter.mbcsToWcs (null, buffer); - return sendIMKeyEvent (type, keyEvent, chars) != null; -} - void sendMouseEvent (int type, int button, int /*long*/ eventPtr) { Event event = new Event (); event.time = OS.gdk_event_get_time (eventPtr); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tracker.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tracker.java index 6e6f340d3c..60bb053aae 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tracker.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tracker.java @@ -37,11 +37,13 @@ import org.eclipse.swt.events.*; */ public class Tracker extends Widget { Composite parent; - int /*long*/ cursor, lastCursor; - boolean tracking, stippled; + int /*long*/ cursor, lastCursor, window; + boolean tracking, cancelled, grabbed, stippled; Rectangle [] rectangles, proportions; Rectangle bounds; int cursorOrientation = SWT.NONE; + int oldX, oldY; + final static int STEPSIZE_SMALL = 1; final static int STEPSIZE_LARGE = 9; @@ -154,6 +156,81 @@ public void addControlListener(ControlListener listener) { } /** + * Adds the listener to the collection of listeners who will + * be notified when keys are pressed and released on the system keyboard, by sending + * it one of the messages defined in the <code>KeyListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see KeyListener + * @see #removeKeyListener + */ +public void addKeyListener(KeyListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener(SWT.KeyUp,typedListener); + addListener(SWT.KeyDown,typedListener); +} + +Point adjustMoveCursor () { + int newX = bounds.x + bounds.width / 2; + int newY = bounds.y; + + display.setCursorLocation (newX, newY); + + /* + * The call to XWarpPointer does not always place the pointer on the + * exact location that is specified, so do a query (below) to get the + * actual location of the pointer after it has been moved. + */ + int [] actualX = new int [1], actualY = new int [1], state = new int [1]; + OS.gdk_window_get_pointer (window, actualX, actualY, state); + return new Point (actualX [0], actualY [0]); +} + +Point adjustResizeCursor () { + int newX, newY; + + if ((cursorOrientation & SWT.LEFT) != 0) { + newX = bounds.x; + } else if ((cursorOrientation & SWT.RIGHT) != 0) { + newX = bounds.x + bounds.width; + } else { + newX = bounds.x + bounds.width / 2; + } + + if ((cursorOrientation & SWT.UP) != 0) { + newY = bounds.y; + } else if ((cursorOrientation & SWT.DOWN) != 0) { + newY = bounds.y + bounds.height; + } else { + newY = bounds.y + bounds.height / 2; + } + + display.setCursorLocation (newX, newY); + + /* + * The call to XWarpPointer does not always place the pointer on the + * exact location that is specified, so do a query (below) to get the + * actual location of the pointer after it has been moved. + */ + int [] actualX = new int [1], actualY = new int [1], state = new int [1]; + OS.gdk_window_get_pointer (window, actualX, actualY, state); + return new Point (actualX [0], actualY [0]); +} + + +/** * Stops displaying the tracker rectangles. Note that this is not considered * to be a cancelation by the user. * @@ -280,6 +357,257 @@ public boolean getStippled () { return stippled; } +boolean grab () { + int result = OS.gdk_pointer_grab (window, false, OS.GDK_POINTER_MOTION_MASK | OS.GDK_BUTTON_RELEASE_MASK, window, cursor, OS.GDK_CURRENT_TIME); + return result == OS.GDK_GRAB_SUCCESS; +} + +int /*long*/ gtk_button_release_event (int /*long*/ widget, int /*long*/ event) { + return gtk_mouse (OS.GDK_BUTTON_RELEASE, widget, event); +} + +int /*long*/ gtk_key_press_event (int /*long*/ widget, int /*long*/ eventPtr) { + int /*long*/ result = super.gtk_key_press_event (widget, eventPtr); + if (result != 0) return result; + GdkEventKey keyEvent = new GdkEventKey (); + OS.memmove (keyEvent, eventPtr, GdkEventKey.sizeof); + int stepSize = ((keyEvent.state & OS.GDK_CONTROL_MASK) != 0) ? STEPSIZE_SMALL : STEPSIZE_LARGE; + int xChange = 0, yChange = 0; + switch (keyEvent.keyval) { + case OS.GDK_Escape: + cancelled = true; + // fallthrough + case OS.GDK_Return: + tracking = false; + break; + case OS.GDK_Left: + xChange = -stepSize; + break; + case OS.GDK_Right: + xChange = stepSize; + break; + case OS.GDK_Up: + yChange = -stepSize; + break; + case OS.GDK_Down: + yChange = stepSize; + break; + } + if (xChange != 0 || yChange != 0) { + Rectangle [] oldRectangles = rectangles; + Rectangle [] rectsToErase = new Rectangle [rectangles.length]; + for (int i = 0; i < rectangles.length; i++) { + Rectangle current = rectangles [i]; + rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height); + } + Event event = new Event (); + event.x = oldX + xChange; + event.y = oldY + yChange; + if ((style & SWT.RESIZE) != 0) { + resizeRectangles (xChange, yChange); + sendEvent (SWT.Resize, event); + /* + * It is possible (but unlikely) that application + * code could have disposed the widget in the resize + * event. If this happens return false to indicate + * that the tracking has failed. + */ + if (isDisposed ()) { + cancelled = true; + return 1; + } + boolean draw = false; + /* + * It is possible that application code could have + * changed the rectangles in the resize event. If this + * happens then only redraw the tracker if the rectangle + * values have changed. + */ + if (rectangles != oldRectangles) { + int length = rectangles.length; + if (length != rectsToErase.length) { + draw = true; + } else { + for (int i = 0; i < length; i++) { + if (!rectangles [i].equals (rectsToErase [i])) { + draw = true; + break; + } + } + } + } else { + draw = true; + } + if (draw) { + drawRectangles (rectsToErase); + drawRectangles (rectangles); + } + Point cursorPos = adjustResizeCursor (); + oldX = cursorPos.x; + oldY = cursorPos.y; + } else { + moveRectangles (xChange, yChange); + sendEvent (SWT.Move, event); + /* + * It is possible (but unlikely) that application + * code could have disposed the widget in the move + * event. If this happens return false to indicate + * that the tracking has failed. + */ + if (isDisposed ()) { + cancelled = true; + return 1; + } + boolean draw = false; + /* + * It is possible that application code could have + * changed the rectangles in the move event. If this + * happens then only redraw the tracker if the rectangle + * values have changed. + */ + if (rectangles != oldRectangles) { + int length = rectangles.length; + if (length != rectsToErase.length) { + draw = true; + } else { + for (int i = 0; i < length; i++) { + if (!rectangles [i].equals (rectsToErase [i])) { + draw = true; + break; + } + } + } + } else { + draw = true; + } + if (draw) { + drawRectangles (rectsToErase); + drawRectangles (rectangles); + } + Point cursorPos = adjustMoveCursor (); + oldX = cursorPos.x; + oldY = cursorPos.y; + } + } + return result; +} + +int /*long*/ gtk_motion_notify_event (int /*long*/ widget, int /*long*/ eventPtr) { + if (cursor != lastCursor) { + ungrab (); + grabbed = grab (); + } + return gtk_mouse (OS.GDK_MOTION_NOTIFY, widget, eventPtr); +} + +int /*long*/ gtk_mouse (int eventType, int /*long*/ widget, int /*long*/ eventPtr) { + int [] newX = new int [1], newY = new int [1]; + OS.gdk_window_get_pointer (window, newX, newY, null); + if (oldX != newX [0] || oldY != newY [0]) { + Rectangle [] oldRectangles = rectangles; + Rectangle [] rectsToErase = new Rectangle [rectangles.length]; + for (int i = 0; i < rectangles.length; i++) { + Rectangle current = rectangles [i]; + rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height); + } + Event event = new Event (); + if (parent == null) { + event.x = newX [0]; + event.y = newY [0]; + } else { + Point screenCoord = display.map (parent, null, newX [0], newY [0]); + event.x = screenCoord.x; + event.y = screenCoord.y; + } + if ((style & SWT.RESIZE) != 0) { + resizeRectangles (newX [0] - oldX, newY [0] - oldY); + sendEvent (SWT.Resize, event); + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the resize + * event. If this happens, return false to indicate + * that the tracking has failed. + */ + if (isDisposed ()) { + cancelled = true; + return 1; + } + boolean draw = false; + /* + * It is possible that application code could have + * changed the rectangles in the resize event. If this + * happens then only redraw the tracker if the rectangle + * values have changed. + */ + if (rectangles != oldRectangles) { + int length = rectangles.length; + if (length != rectsToErase.length) { + draw = true; + } else { + for (int i = 0; i < length; i++) { + if (!rectangles [i].equals (rectsToErase [i])) { + draw = true; + break; + } + } + } + } else { + draw = true; + } + if (draw) { + drawRectangles (rectsToErase); + drawRectangles (rectangles); + } + Point cursorPos = adjustResizeCursor (); + newX [0] = cursorPos.x; + newY [0] = cursorPos.y; + } else { + moveRectangles (newX [0] - oldX, newY [0] - oldY); + sendEvent (SWT.Move, event); + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the move + * event. If this happens, return false to indicate + * that the tracking has failed. + */ + if (isDisposed ()) { + cancelled = true; + return 1; + } + boolean draw = false; + /* + * It is possible that application code could have + * changed the rectangles in the move event. If this + * happens then only redraw the tracker if the rectangle + * values have changed. + */ + if (rectangles != oldRectangles) { + int length = rectangles.length; + if (length != rectsToErase.length) { + draw = true; + } else { + for (int i = 0; i < length; i++) { + if (!rectangles [i].equals (rectsToErase [i])) { + draw = true; + break; + } + } + } + } else { + draw = true; + } + if (draw) { + drawRectangles (rectsToErase); + drawRectangles (rectangles); + } + } + oldX = newX [0]; + oldY = newY [0]; + } + tracking = eventType != OS.GDK_BUTTON_RELEASE; + return 0; +} + void moveRectangles (int xChange, int yChange) { if (xChange < 0 && ((style & SWT.LEFT) == 0)) xChange = 0; if (xChange > 0 && ((style & SWT.RIGHT) == 0)) xChange = 0; @@ -308,12 +636,12 @@ void moveRectangles (int xChange, int yChange) { public boolean open () { checkWidget(); if (rectangles == null) return false; - int /*long*/ window = OS.GDK_ROOT_PARENT (); + window = OS.GDK_ROOT_PARENT (); if (parent != null) { window = OS.GTK_WIDGET_WINDOW (parent.paintHandle()); } if (window == 0) return false; - boolean cancelled = false; + cancelled = false; tracking = true; drawRectangles (rectangles); int [] oldX = new int [1], oldY = new int [1], state = new int [1]; @@ -332,34 +660,26 @@ public boolean open () { cursorOrientation |= hStyle; } - /* - * The following is intentionally commented. Since gtk does not currently - * support pointer warping, the resize cursor cannot be adjusted. If this - * capability is added in the future then the following should be uncommented, - * and the #adjustResizeCursor method can be copied from another platform. - */ -// Point cursorPos; -// int mask = OS.GDK_BUTTON1_MASK | OS.GDK_BUTTON2_MASK | OS.GDK_BUTTON3_MASK; -// boolean mouseDown = (state [0] & mask) != 0; -// if (!mouseDown) { -// if ((style & SWT.RESIZE) != 0) { -// cursorPos = adjustResizeCursor (xDisplay, xWindow); -// } else { -// cursorPos = adjustMoveCursor (xDisplay, xWindow); -// } -// oldX [0] = cursorPos.x; oldY [0] = cursorPos.y; -// } + Point cursorPos; + int mask = OS.GDK_BUTTON1_MASK | OS.GDK_BUTTON2_MASK | OS.GDK_BUTTON3_MASK; + boolean mouseDown = (state [0] & mask) != 0; + if (!mouseDown) { + if ((style & SWT.RESIZE) != 0) { + cursorPos = adjustResizeCursor (); + } else { + cursorPos = adjustMoveCursor (); + } + oldX [0] = cursorPos.x; + oldY [0] = cursorPos.y; + } + this.oldX = oldX [0]; + this.oldY = oldY [0]; - GdkEvent gdkEvent = new GdkEvent(); - GdkEventKey keyEvent = new GdkEventKey (); - int [] newX = new int [1], newY = new int [1]; - int grabMask = OS.GDK_POINTER_MOTION_MASK | OS.GDK_BUTTON_RELEASE_MASK; - int ptrGrabResult = OS.gdk_pointer_grab (window, false, grabMask, window, cursor, OS.GDK_CURRENT_TIME); + grabbed = grab (); lastCursor = cursor; - /* - * Tracker behaves like a Dialog with its own OS event loop. - */ + /* Tracker behaves like a Dialog with its own OS event loop. */ + GdkEvent gdkEvent = new GdkEvent(); while (tracking) { if (parent != null && parent.isDisposed ()) break; int /*long*/ eventPtr; @@ -372,257 +692,15 @@ public boolean open () { } } OS.memmove (gdkEvent, eventPtr, GdkEvent.sizeof); - int eventType = gdkEvent.type; - switch (eventType) { - case OS.GDK_MOTION_NOTIFY: - if (cursor != lastCursor) { - if (ptrGrabResult == OS.GDK_GRAB_SUCCESS) OS.gdk_pointer_ungrab (OS.GDK_CURRENT_TIME); - ptrGrabResult = OS.gdk_pointer_grab (window, false, grabMask, window, cursor, OS.GDK_CURRENT_TIME); - } - // fall through - case OS.GDK_BUTTON_RELEASE: - OS.gdk_window_get_pointer (window, newX, newY, null); - if (oldX [0] != newX [0] || oldY [0] != newY [0]) { - Rectangle [] oldRectangles = rectangles; - Rectangle [] rectsToErase = new Rectangle [rectangles.length]; - for (int i = 0; i < rectangles.length; i++) { - Rectangle current = rectangles [i]; - rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height); - } - Event event = new Event (); - if (parent == null) { - event.x = newX [0]; - event.y = newY [0]; - } else { - Point screenCoord = display.map (parent, null, newX [0], newY [0]); - event.x = screenCoord.x; - event.y = screenCoord.y; - } - if ((style & SWT.RESIZE) != 0) { - resizeRectangles (newX [0] - oldX [0], newY [0] - oldY [0]); - sendEvent (SWT.Resize, event); - /* - * It is possible (but unlikely), that application - * code could have disposed the widget in the resize - * event. If this happens, return false to indicate - * that the tracking has failed. - */ - if (isDisposed ()) { - cancelled = true; - break; - } - boolean draw = false; - /* - * It is possible that application code could have - * changed the rectangles in the resize event. If this - * happens then only redraw the tracker if the rectangle - * values have changed. - */ - if (rectangles != oldRectangles) { - int length = rectangles.length; - if (length != rectsToErase.length) { - draw = true; - } else { - for (int i = 0; i < length; i++) { - if (!rectangles [i].equals (rectsToErase [i])) { - draw = true; - break; - } - } - } - } else { - draw = true; - } - if (draw) { - drawRectangles (rectsToErase); - drawRectangles (rectangles); - } - /* - * The following is intentionally commented. Since gtk does not currently - * support pointer warping, the resize cursor cannot be adjusted. If this - * capability is added in the future then the following should be uncommented, - * and the #adjustResizeCursor method can be copied from another platform. - */ -// Point cursorPos = adjustResizeCursor (); - } else { - moveRectangles (newX [0] - oldX [0], newY [0] - oldY [0]); - sendEvent (SWT.Move, event); - /* - * It is possible (but unlikely), that application - * code could have disposed the widget in the move - * event. If this happens, return false to indicate - * that the tracking has failed. - */ - if (isDisposed ()) { - cancelled = true; - break; - } - boolean draw = false; - /* - * It is possible that application code could have - * changed the rectangles in the move event. If this - * happens then only redraw the tracker if the rectangle - * values have changed. - */ - if (rectangles != oldRectangles) { - int length = rectangles.length; - if (length != rectsToErase.length) { - draw = true; - } else { - for (int i = 0; i < length; i++) { - if (!rectangles [i].equals (rectsToErase [i])) { - draw = true; - break; - } - } - } - } else { - draw = true; - } - if (draw) { - drawRectangles (rectsToErase); - drawRectangles (rectangles); - } - } - oldX [0] = newX [0]; oldY [0] = newY [0]; - } - tracking = eventType != OS.GDK_BUTTON_RELEASE; - break; - case OS.GDK_KEY_PRESS: - OS.memmove (keyEvent, eventPtr, GdkEventKey.sizeof); - int stepSize = ((keyEvent.state & OS.GDK_CONTROL_MASK) != 0) ? STEPSIZE_SMALL : STEPSIZE_LARGE; - int xChange = 0, yChange = 0; - switch (keyEvent.keyval) { - case OS.GDK_Escape: - cancelled = true; - // fallthrough - case OS.GDK_Return: - tracking = false; - break; - case OS.GDK_Left: - xChange = -stepSize; - break; - case OS.GDK_Right: - xChange = stepSize; - break; - case OS.GDK_Up: - yChange = -stepSize; - break; - case OS.GDK_Down: - yChange = stepSize; - break; - } - if (xChange != 0 || yChange != 0) { - Rectangle [] oldRectangles = rectangles; - Rectangle [] rectsToErase = new Rectangle [rectangles.length]; - for (int i = 0; i < rectangles.length; i++) { - Rectangle current = rectangles [i]; - rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height); - } - Event event = new Event (); - event.x = oldX [0] + xChange; - event.y = oldY [0] + yChange; - if ((style & SWT.RESIZE) != 0) { - resizeRectangles (xChange, yChange); - sendEvent (SWT.Resize, event); - /* - * It is possible (but unlikely) that application - * code could have disposed the widget in the resize - * event. If this happens return false to indicate - * that the tracking has failed. - */ - if (isDisposed ()) { - cancelled = true; - break; - } - boolean draw = false; - /* - * It is possible that application code could have - * changed the rectangles in the resize event. If this - * happens then only redraw the tracker if the rectangle - * values have changed. - */ - if (rectangles != oldRectangles) { - int length = rectangles.length; - if (length != rectsToErase.length) { - draw = true; - } else { - for (int i = 0; i < length; i++) { - if (!rectangles [i].equals (rectsToErase [i])) { - draw = true; - break; - } - } - } - } else { - draw = true; - } - if (draw) { - drawRectangles (rectsToErase); - drawRectangles (rectangles); - } - /* - * The following is intentionally commented. Since gtk does not currently - * support pointer warping, the resize cursor cannot be adjusted. If this - * capability is added in the future then the following should be uncommented, - * and the #adjustResizeCursor method can be copied from another platform. - */ -// cursorPos = adjustResizeCursor (xDisplay, xWindow); -// oldX[0] = cursorPos.x; oldY[0] = cursorPos.y; - } else { - moveRectangles (xChange, yChange); - sendEvent (SWT.Move, event); - /* - * It is possible (but unlikely) that application - * code could have disposed the widget in the move - * event. If this happens return false to indicate - * that the tracking has failed. - */ - if (isDisposed ()) { - cancelled = true; - break; - } - boolean draw = false; - /* - * It is possible that application code could have - * changed the rectangles in the move event. If this - * happens then only redraw the tracker if the rectangle - * values have changed. - */ - if (rectangles != oldRectangles) { - int length = rectangles.length; - if (length != rectsToErase.length) { - draw = true; - } else { - for (int i = 0; i < length; i++) { - if (!rectangles [i].equals (rectsToErase [i])) { - draw = true; - break; - } - } - } - } else { - draw = true; - } - if (draw) { - drawRectangles (rectsToErase); - drawRectangles (rectangles); - } - /* - * The following is intentionally commented. Since gtk does not currently - * support pointer warping, the move cursor cannot be adjusted. If this - * capability is added in the future then the following should be uncommented, - * and the #adjustMoveCursor method can be copied from another platform. - */ -// cursorPos = adjustMoveCursor (xDisplay, xWindow); -// oldX[0] = cursorPos.x; oldY[0] = cursorPos.y; - } - } - break; + int /*long*/ widget = OS.gtk_get_event_widget (eventPtr); + switch (gdkEvent.type) { + case OS.GDK_MOTION_NOTIFY: gtk_motion_notify_event (widget, eventPtr); break; + case OS.GDK_BUTTON_RELEASE: gtk_button_release_event (widget, eventPtr); break; + case OS.GDK_KEY_PRESS: gtk_key_press_event (widget, eventPtr); break; + case OS.GDK_KEY_RELEASE: gtk_key_release_event (widget, eventPtr); break; case OS.GDK_BUTTON_PRESS: case OS.GDK_2BUTTON_PRESS: case OS.GDK_3BUTTON_PRESS: - case OS.GDK_KEY_RELEASE: case OS.GDK_ENTER_NOTIFY: case OS.GDK_LEAVE_NOTIFY: /* Do not dispatch these */ @@ -633,7 +711,8 @@ public boolean open () { OS.gdk_event_free (eventPtr); } if (!isDisposed ()) drawRectangles (rectangles); - if (ptrGrabResult == OS.GDK_GRAB_SUCCESS) OS.gdk_pointer_ungrab (OS.GDK_CURRENT_TIME); + ungrab (); + window = 0; return !cancelled; } @@ -662,6 +741,31 @@ public void removeControlListener (ControlListener listener) { eventTable.unhook (SWT.Move, listener); } +/** + * Removes the listener from the collection of listeners who will + * be notified when keys are pressed and released on the system keyboard. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see KeyListener + * @see #addKeyListener + */ +public void removeKeyListener(KeyListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.KeyUp, listener); + eventTable.unhook (SWT.KeyDown, listener); +} + void resizeRectangles (int xChange, int yChange) { /* * If the cursor orientation has not been set in the orientation of @@ -829,4 +933,8 @@ public void setStippled (boolean stippled) { this.stippled = stippled; } +void ungrab () { + if (grabbed) OS.gdk_pointer_ungrab (OS.GDK_CURRENT_TIME); +} + } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Widget.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Widget.java index 344c57bab8..4148acc376 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Widget.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Widget.java @@ -580,11 +580,15 @@ int /*long*/ gtk_insert_text (int /*long*/ widget, int /*long*/ new_text, int /* } int /*long*/ gtk_key_press_event (int /*long*/ widget, int /*long*/ event) { - return 0; + GdkEventKey gdkEvent = new GdkEventKey (); + OS.memmove (gdkEvent, event, GdkEventKey.sizeof); + return sendKeyEvent (SWT.KeyDown, gdkEvent) ? 0 : 1; } int /*long*/ gtk_key_release_event (int /*long*/ widget, int /*long*/ event) { - return 0; + GdkEventKey gdkEvent = new GdkEventKey (); + OS.memmove (gdkEvent, event, GdkEventKey.sizeof); + return sendKeyEvent (SWT.KeyUp, gdkEvent) ? 0 : 1; } int /*long*/ gtk_leave_notify_event (int /*long*/ widget, int /*long*/ event) { @@ -1000,6 +1004,86 @@ void sendEvent (int eventType, Event event, boolean send) { } } +boolean sendKeyEvent (int type, GdkEventKey keyEvent) { + int length = keyEvent.length; + if (length <= 1) { + Event event = new Event (); + event.time = keyEvent.time; + if (!setKeyState (event, keyEvent)) return true; + sendEvent (type, event); + // widget could be disposed at this point + + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the key + * events. If this happens, end the processing of + * the key by returning false. + */ + if (isDisposed ()) return false; + return event.doit; + } + byte [] buffer = new byte [length]; + OS.memmove (buffer, keyEvent.string, length); + char [] chars = Converter.mbcsToWcs (null, buffer); + return sendIMKeyEvent (type, keyEvent, chars) != null; +} + +char [] sendIMKeyEvent (int type, GdkEventKey keyEvent, char [] chars) { + int index = 0, count = 0, state = 0, time = 0; + if (keyEvent == null) { + int /*long*/ ptr = OS.gtk_get_current_event (); + if (ptr != 0) { + keyEvent = new GdkEventKey (); + OS.memmove (keyEvent, ptr, GdkEventKey.sizeof); + OS.gdk_event_free (ptr); + switch (keyEvent.type) { + case OS.GDK_KEY_PRESS: + case OS.GDK_KEY_RELEASE: + state = keyEvent.state; + time = keyEvent.time; + break; + default: + keyEvent = null; + break; + } + } + } + if (keyEvent == null) { + int [] buffer = new int [1]; + OS.gtk_get_current_event_state (buffer); + state = buffer [0]; + time = OS.gtk_get_current_event_time(); + } + while (index < chars.length) { + Event event = new Event (); + event.time = time; + if (keyEvent != null && keyEvent.length <= 1) { + setKeyState (event, keyEvent); + } else { + setInputState (event, state); + } + event.character = chars [index]; + sendEvent (type, event); + + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the key + * events. If this happens, end the processing of + * the key by returning null. + */ + if (isDisposed ()) return null; + if (event.doit) chars [count++] = chars [index]; + index++; + } + if (count == 0) return null; + if (index != count) { + char [] result = new char [count]; + System.arraycopy (chars, 0, result, 0, count); + return result; + } + return chars; +} + /** * Sets the application defined widget data associated * with the receiver to be the argument. The <em>widget diff --git a/bundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Control.java index 1a08b9f572..0eb1e7b6b2 100755 --- a/bundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Control.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Control.java @@ -11,7 +11,6 @@ package org.eclipse.swt.widgets; -import org.eclipse.swt.internal.*; import org.eclipse.swt.internal.motif.*; import org.eclipse.swt.*; import org.eclipse.swt.graphics.*; @@ -1705,106 +1704,6 @@ void sendHelpEvent (int callData) { control = control.parent; } } -boolean sendIMKeyEvent (int type, XKeyEvent xEvent) { - return sendIMKeyEvent (type, xEvent, 0); -} -boolean sendIMKeyEvent (int type, XKeyEvent xEvent, int textHandle) { - /* - * Bug in Motif. On Linux only, XmImMbLookupString () does not return - * XBufferOverflow as the status if the buffer is too small. The fix - * is to pass a large buffer. - */ - byte [] buffer = new byte [512]; - int [] status = new int [1], unused = new int [1]; - int focusHandle = OS.XtWindowToWidget (xEvent.display, xEvent.window); - int length = OS.XmImMbLookupString (focusHandle, xEvent, buffer, buffer.length, unused, status); - if (status [0] == OS.XBufferOverflow) { - buffer = new byte [length]; - length = OS.XmImMbLookupString (focusHandle, xEvent, buffer, length, unused, status); - } - if (length == 0) return true; - - /* Convert from MBCS to UNICODE and send the event */ - /* Use the character encoding for the default locale */ - char [] chars = Converter.mbcsToWcs (null, buffer); - int index = 0, count = 0; - while (index < chars.length) { - if (chars [index] == 0) { - chars [count] = 0; - break; - } - Event event = new Event (); - event.time = xEvent.time; - event.character = chars [index]; - setInputState (event, xEvent.state); - sendEvent (type, event); - // widget could be disposed at this point - - /* - * It is possible (but unlikely), that application - * code could have disposed the widget in the key - * events. If this happens, end the processing of - * the key by returning false. - */ - if (isDisposed ()) return false; - if (event.doit) chars [count++] = chars [index]; - index++; - } - if (count == 0) return false; - if (textHandle != 0) { - /* - * Bug in Motif. On Solaris and Linux, XmImMbLookupString() clears - * the characters from the IME. This causes the characters to be - * stolen from the text widget. The fix is to detect that the IME - * has been cleared and use XmTextInsert() to insert the stolen - * characters. This problem does not happen on AIX. - */ - byte [] testBuffer = new byte [5]; - int testLength = OS.XmImMbLookupString (textHandle, xEvent, testBuffer, testBuffer.length, unused, unused); - if (testLength == 0 || index != count) { - int [] start = new int [1], end = new int [1]; - OS.XmTextGetSelectionPosition (textHandle, start, end); - if (start [0] == end [0]) { - start [0] = end [0] = OS.XmTextGetInsertionPosition (textHandle); - } - boolean warnings = display.getWarnings (); - display.setWarnings (false); - if (index != count) { - buffer = Converter.wcsToMbcs (getCodePage (), chars, true); - } - OS.XmTextReplace (textHandle, start [0], end [0], buffer); - int position = start [0] + count; - OS.XmTextSetInsertionPosition (textHandle, position); - display.setWarnings (warnings); - return false; - } - } - return true; -} -boolean sendKeyEvent (int type, XKeyEvent xEvent) { - Event event = new Event (); - event.time = xEvent.time; - if (!setKeyState (event, xEvent)) return true; - Control control = this; - if ((state & CANVAS) != 0) { - if ((style & SWT.NO_FOCUS) != 0) { - control = display.getFocusControl (); - } - } - if (control != null) { - control.sendEvent (type, event); - // widget could be disposed at this point - - /* - * It is possible (but unlikely), that application - * code could have disposed the widget in the key - * events. If this happens, end the processing of - * the key by returning false. - */ - if (isDisposed ()) return false; - } - return event.doit; -} void sendMouseEvent (int type) { int xDisplay = OS.XtDisplay (handle), xWindow = OS.XtWindow (handle); int [] windowX = new int [1], windowY = new int [1], mask = new int [1], unused = new int [1]; @@ -3121,21 +3020,6 @@ int xFocusOut (XFocusChangeEvent xEvent) { } return 0; } -int XKeyPress (int w, int client_data, int call_data, int continue_to_dispatch) { - XKeyEvent xEvent = new XKeyEvent (); - OS.memmove (xEvent, call_data, XKeyEvent.sizeof); - boolean doit = true; - if (xEvent.keycode != 0) { - doit = sendKeyEvent (SWT.KeyDown, xEvent); - } else { - doit = sendIMKeyEvent (SWT.KeyDown, xEvent); - } - if (!doit) { - OS.memmove (continue_to_dispatch, new int [1], 4); - return 1; - } - return 0; -} int XKeyRelease (int w, int client_data, int call_data, int continue_to_dispatch) { XKeyEvent xEvent = new XKeyEvent (); OS.memmove (xEvent, call_data, XKeyEvent.sizeof); @@ -3147,11 +3031,7 @@ int XKeyRelease (int w, int client_data, int call_data, int continue_to_dispatch showMenu (xEvent.x_root, xEvent.y_root); } } - if (!sendKeyEvent (SWT.KeyUp, xEvent)) { - OS.memmove (continue_to_dispatch, new int [1], 4); - return 1; - } - return 0; + return super.XKeyRelease (w, client_data, call_data, continue_to_dispatch); } int XLeaveWindow (int w, int client_data, int call_data, int continue_to_dispatch) { display.removeMouseHoverTimeOut (); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Tracker.java b/bundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Tracker.java index 09e24b1b98..fe26955ae6 100755 --- a/bundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Tracker.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Tracker.java @@ -37,11 +37,12 @@ import org.eclipse.swt.events.*; */ public class Tracker extends Widget { Composite parent; - boolean tracking, stippled; + boolean tracking, cancelled, stippled; Rectangle [] rectangles, proportions; Rectangle bounds; int cursorOrientation = SWT.NONE; - int cursor; + int cursor, window, oldX, oldY; + final static int STEPSIZE_SMALL = 1; final static int STEPSIZE_LARGE = 9; @@ -152,8 +153,34 @@ public void addControlListener (ControlListener listener) { addListener (SWT.Resize, typedListener); addListener (SWT.Move, typedListener); } +/** + * Adds the listener to the collection of listeners who will + * be notified when keys are pressed and released on the system keyboard, by sending + * it one of the messages defined in the <code>KeyListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see KeyListener + * @see #removeKeyListener + */ +public void addKeyListener(KeyListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener(SWT.KeyUp,typedListener); + addListener(SWT.KeyDown,typedListener); +} -Point adjustMoveCursor (int xDisplay, int xWindow) { +Point adjustMoveCursor () { final int unused[] = new int[1]; int actualX[] = new int[1]; int actualY[] = new int[1]; @@ -161,16 +188,17 @@ Point adjustMoveCursor (int xDisplay, int xWindow) { int newX = bounds.x + bounds.width / 2; int newY = bounds.y; - OS.XWarpPointer (xDisplay, OS.None, xWindow, 0, 0, 0, 0, newX, newY); + int xDisplay = display.xDisplay; + OS.XWarpPointer (xDisplay, OS.None, window, 0, 0, 0, 0, newX, newY); /* * The call to XWarpPointer does not always place the pointer on the * exact location that is specified, so do a query (below) to get the * actual location of the pointer after it has been moved. */ - OS.XQueryPointer (xDisplay, xWindow, unused, unused, actualX, actualY, unused, unused, unused); + OS.XQueryPointer (xDisplay, window, unused, unused, actualX, actualY, unused, unused, unused); return new Point (actualX[0], actualY[0]); } -Point adjustResizeCursor (int xDisplay, int xWindow) { +Point adjustResizeCursor () { int newX, newY; if ((cursorOrientation & SWT.LEFT) != 0) { @@ -192,13 +220,14 @@ Point adjustResizeCursor (int xDisplay, int xWindow) { final int unused[] = new int[1]; int actualX[] = new int[1]; int actualY[] = new int[1]; - OS.XWarpPointer (xDisplay, 0, xWindow, 0, 0, 0, 0, newX, newY); + int xDisplay = display.xDisplay; + OS.XWarpPointer (xDisplay, 0, window, 0, 0, 0, 0, newX, newY); /* * The call to XWarpPointer does not always place the pointer on the * exact location that is specified, so do a query (below) to get the * actual location of the pointer after it has been moved. */ - OS.XQueryPointer (xDisplay, xWindow, unused, unused, actualX, actualY, unused, unused, unused); + OS.XQueryPointer (xDisplay, window, unused, unused, actualX, actualY, unused, unused, unused); return new Point (actualX[0], actualY[0]); } static int checkStyle (int style) { @@ -365,17 +394,17 @@ public boolean open () { checkWidget (); if (rectangles == null) return false; int xDisplay = display.xDisplay; - int xWindow = OS.XDefaultRootWindow (xDisplay); + window = OS.XDefaultRootWindow (xDisplay); if (parent != null) { - xWindow = OS.XtWindow (parent.handle); - if (xWindow == 0) return false; + window = OS.XtWindow (parent.handle); + if (window == 0) return false; } - boolean cancelled = false; + cancelled = false; tracking = true; drawRectangles (rectangles, stippled); int [] oldX = new int [1], oldY = new int [1]; int [] unused = new int [1], mask = new int [1]; - OS.XQueryPointer (xDisplay, xWindow, unused, unused, oldX, oldY, unused, unused, mask); + OS.XQueryPointer (xDisplay, window, unused, unused, oldX, oldY, unused, unused, mask); /* * If exactly one of UP/DOWN is specified as a style then set the cursor @@ -395,288 +424,37 @@ public boolean open () { boolean mouseDown = (mask [0] & mouseMasks) != 0; if (!mouseDown) { if ((style & SWT.RESIZE) != 0) { - cursorPos = adjustResizeCursor (xDisplay, xWindow); + cursorPos = adjustResizeCursor (); } else { - cursorPos = adjustMoveCursor (xDisplay, xWindow); + cursorPos = adjustMoveCursor (); } oldX [0] = cursorPos.x; oldY [0] = cursorPos.y; } + this.oldX = oldX [0]; + this.oldY = oldY [0]; - int xEvent = OS.XtMalloc (XEvent.sizeof); - XEvent anyEvent = new XEvent(); - int [] newX = new int [1], newY = new int [1]; - int xtContext = OS.XtDisplayToApplicationContext (xDisplay); - - int ptrGrabResult = OS.XGrabPointer ( - xDisplay, - xWindow, - 0, + int ptrGrabResult = OS.XGrabPointer (xDisplay, window, 0, OS.ButtonPressMask | OS.ButtonReleaseMask | OS.PointerMotionMask, - OS.GrabModeAsync, - OS.GrabModeAsync, - OS.None, - OS.None, - OS.CurrentTime); - int kbdGrabResult = OS.XGrabKeyboard ( - xDisplay, - xWindow, - 0, - OS.GrabModeAsync, - OS.GrabModeAsync, - OS.CurrentTime); + OS.GrabModeAsync, OS.GrabModeAsync, OS.None, OS.None, OS.CurrentTime); + int kbdGrabResult = OS.XGrabKeyboard (xDisplay, window, 0, + OS.GrabModeAsync, OS.GrabModeAsync, OS.CurrentTime); - /* - * Tracker behaves like a Dialog with its own OS event loop. - */ + /* Tracker behaves like a Dialog with its own OS event loop. */ + XAnyEvent anyEvent = new XAnyEvent(); + int xEvent = OS.XtMalloc (XEvent.sizeof); + int dispatch = OS.XtMalloc (4); + int xtContext = OS.XtDisplayToApplicationContext (xDisplay); while (tracking) { if (parent != null && parent.isDisposed ()) break; OS.XtAppNextEvent (xtContext, xEvent); - OS.memmove (anyEvent, xEvent, XEvent.sizeof); + OS.memmove (anyEvent, xEvent, XAnyEvent.sizeof); + int widget = OS.XtWindowToWidget (anyEvent.display, anyEvent.window); switch (anyEvent.type) { - case OS.MotionNotify: - if (cursor != 0) { - OS.XChangeActivePointerGrab ( - xDisplay, - OS.ButtonPressMask | OS.ButtonReleaseMask | OS.PointerMotionMask, - cursor, - OS.CurrentTime); - } - // fall through - case OS.ButtonRelease: - OS.XQueryPointer (xDisplay, xWindow, unused, unused, newX, newY, unused, unused, unused); - if (oldX [0] != newX [0] || oldY [0] != newY [0]) { - Rectangle [] oldRectangles = rectangles; - boolean oldStippled = stippled; - Rectangle [] rectsToErase = new Rectangle [rectangles.length]; - for (int i = 0; i < rectangles.length; i++) { - Rectangle current = rectangles [i]; - rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height); - } - Event event = new Event (); - event.x = newX [0]; - event.y = newY [0]; - if ((style & SWT.RESIZE) != 0) { - resizeRectangles (newX [0] - oldX [0], newY [0] - oldY [0]); - sendEvent (SWT.Resize, event); - /* - * It is possible (but unlikely) that application code - * could have disposed the widget in the resize event. - * If this happens then return false to indicate that - * the move failed. - */ - if (isDisposed ()) { - cancelled = true; - break; - } - boolean draw = false; - /* - * It is possible that application code could have - * changed the rectangles in the resize event. If this - * happens then only redraw the tracker if the rectangle - * values have changed. - */ - if (rectangles != oldRectangles) { - int length = rectangles.length; - if (length != rectsToErase.length) { - draw = true; - } else { - for (int i = 0; i < length; i++) { - if (!rectangles [i].equals (rectsToErase [i])) { - draw = true; - break; - } - } - } - } else { - draw = true; - } - if (draw) { - drawRectangles (rectsToErase, oldStippled); - drawRectangles (rectangles, stippled); - } - cursorPos = adjustResizeCursor (xDisplay, xWindow); - newX [0] = cursorPos.x; newY [0] = cursorPos.y; - } else { - moveRectangles (newX [0] - oldX [0], newY [0] - oldY [0]); - sendEvent (SWT.Move, event); - /* - * It is possible (but unlikely) that application code - * could have disposed the widget in the move event. - * If this happens then return false to indicate that - * the move failed. - */ - if (isDisposed ()) { - cancelled = true; - break; - } - boolean draw = false; - /* - * It is possible that application code could have - * changed the rectangles in the move event. If this - * happens then only redraw the tracker if the rectangle - * values have changed. - */ - if (rectangles != oldRectangles) { - int length = rectangles.length; - if (length != rectsToErase.length) { - draw = true; - } else { - for (int i = 0; i < length; i++) { - if (!rectangles [i].equals (rectsToErase [i])) { - draw = true; - break; - } - } - } - } else { - draw = true; - } - if (draw) { - drawRectangles (rectsToErase, oldStippled); - drawRectangles (rectangles, stippled); - } - } - oldX [0] = newX [0]; oldY [0] = newY [0]; - } - tracking = anyEvent.type != OS.ButtonRelease; - break; - case OS.KeyPress: - XKeyEvent keyEvent = new XKeyEvent (); - OS.memmove (keyEvent, xEvent, XKeyEvent.sizeof); - if (keyEvent.keycode != 0) { - int [] keysym = new int [1]; - OS.XLookupString (keyEvent, null, 0, keysym, null); - keysym [0] &= 0xFFFF; - int xChange = 0, yChange = 0; - int stepSize = ((keyEvent.state & OS.ControlMask) != 0) ? STEPSIZE_SMALL : STEPSIZE_LARGE; - switch (keysym [0]) { - case OS.XK_KP_Enter: - case OS.XK_Return: - tracking = false; - /* - * Eat the subsequent KeyRelease event - */ - OS.XtAppNextEvent (xtContext, xEvent); - break; - case OS.XK_Escape: - tracking = false; - cancelled = true; - /* - * Eat the subsequent KeyRelease event - */ - OS.XtAppNextEvent (xtContext, xEvent); - break; - case OS.XK_Left: - xChange = -stepSize; - break; - case OS.XK_Right: - xChange = stepSize; - break; - case OS.XK_Up: - yChange = -stepSize; - break; - case OS.XK_Down: - yChange = stepSize; - break; - } - if (xChange != 0 || yChange != 0) { - Rectangle [] oldRectangles = rectangles; - boolean oldStippled = stippled; - Rectangle [] rectsToErase = new Rectangle [rectangles.length]; - for (int i = 0; i < rectangles.length; i++) { - Rectangle current = rectangles [i]; - rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height); - } - Event event = new Event (); - event.x = oldX [0] + xChange; - event.y = oldY [0] + yChange; - if ((style & SWT.RESIZE) != 0) { - resizeRectangles (xChange, yChange); - sendEvent (SWT.Resize, event); - /* - * It is possible (but unlikely) that application code - * could have disposed the widget in the resize event. - * If this happens then return false to indicate that - * the move failed. - */ - if (isDisposed ()) { - cancelled = true; - break; - } - boolean draw = false; - /* - * It is possible that application code could have - * changed the rectangles in the resize event. If this - * happens then only redraw the tracker if the rectangle - * values have changed. - */ - if (rectangles != oldRectangles) { - int length = rectangles.length; - if (length != rectsToErase.length) { - draw = true; - } else { - for (int i = 0; i < length; i++) { - if (!rectangles [i].equals (rectsToErase [i])) { - draw = true; - break; - } - } - } - } else { - draw = true; - } - if (draw) { - drawRectangles (rectsToErase, oldStippled); - drawRectangles (rectangles, stippled); - } - cursorPos = adjustResizeCursor (xDisplay, xWindow); - } else { - moveRectangles (xChange, yChange); - sendEvent (SWT.Move, event); - /* - * It is possible (but unlikely) that application code - * could have disposed the widget in the move event. - * If this happens then return false to indicate that - * the move failed. - */ - if (isDisposed ()) { - cancelled = true; - break; - } - boolean draw = false; - /* - * It is possible that application code could have - * changed the rectangles in the move event. If this - * happens then only redraw the tracker if the rectangle - * values have changed. - */ - if (rectangles != oldRectangles) { - int length = rectangles.length; - if (length != rectsToErase.length) { - draw = true; - } else { - for (int i = 0; i < length; i++) { - if (!rectangles [i].equals (rectsToErase [i])) { - draw = true; - break; - } - } - } - } else { - draw = true; - } - if (draw) { - drawRectangles (rectsToErase, oldStippled); - drawRectangles (rectangles, stippled); - } - cursorPos = adjustMoveCursor (xDisplay, xWindow); - } - oldX [0] = cursorPos.x; oldY [0] = cursorPos.y; - } - } - break; + case OS.MotionNotify: XPointerMotion (widget, 0, xEvent, dispatch); break; + case OS.ButtonRelease: XButtonRelease (widget, 0, xEvent, dispatch); break; + case OS.KeyPress: XKeyPress (widget, 0, xEvent, dispatch); break; + case OS.KeyRelease: XKeyRelease (widget, 0, xEvent, dispatch); break; case OS.ButtonPress: - case OS.KeyRelease: case OS.EnterNotify: case OS.LeaveNotify: /* Do not dispatch these */ @@ -685,10 +463,12 @@ public boolean open () { OS.XtDispatchEvent (xEvent); } } - OS.XtFree (xEvent); + if (xEvent != 0) OS.XtFree (xEvent); + if (dispatch != 0) OS.XtFree (dispatch); if (!isDisposed()) drawRectangles (rectangles, stippled); if (ptrGrabResult == OS.GrabSuccess) OS.XUngrabPointer (xDisplay, OS.CurrentTime); if (kbdGrabResult == OS.GrabSuccess) OS.XUngrabKeyboard (xDisplay, OS.CurrentTime); + window = 0; return !cancelled; } /** @@ -715,6 +495,30 @@ public void removeControlListener (ControlListener listener) { eventTable.unhook (SWT.Resize, listener); eventTable.unhook (SWT.Move, listener); } +/** + * Removes the listener from the collection of listeners who will + * be notified when keys are pressed and released on the system keyboard. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see KeyListener + * @see #addKeyListener + */ +public void removeKeyListener(KeyListener listener) { + checkWidget(); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook(SWT.KeyUp, listener); + eventTable.unhook(SWT.KeyDown, listener); +} void resizeRectangles (int xChange, int yChange) { /* * If the cursor orientation has not been set in the orientation of @@ -879,4 +683,260 @@ public void setStippled (boolean stippled) { checkWidget (); this.stippled = stippled; } + +int XButtonRelease (int w, int client_data, int call_data, int continue_to_dispatch) { + return xMouse (OS.ButtonRelease, w, client_data, call_data, continue_to_dispatch); +} + +int XKeyPress (int w, int client_data, int call_data, int continue_to_dispatch) { + int result = super.XKeyPress (w, client_data, call_data, continue_to_dispatch); + if (result != 0) return result; + XKeyEvent keyEvent = new XKeyEvent (); + OS.memmove (keyEvent, call_data, XKeyEvent.sizeof); + if (keyEvent.keycode != 0) { + int [] keysym = new int [1]; + OS.XLookupString (keyEvent, null, 0, keysym, null); + keysym [0] &= 0xFFFF; + int xChange = 0, yChange = 0; + int stepSize = ((keyEvent.state & OS.ControlMask) != 0) ? STEPSIZE_SMALL : STEPSIZE_LARGE; + switch (keysym [0]) { + case OS.XK_KP_Enter: + case OS.XK_Return: + tracking = false; + /* Eat the subsequent KeyRelease event */ + OS.XtAppNextEvent (OS.XtDisplayToApplicationContext (keyEvent.display), call_data); + break; + case OS.XK_Escape: + tracking = false; + cancelled = true; + /* Eat the subsequent KeyRelease event */ + OS.XtAppNextEvent (OS.XtDisplayToApplicationContext (keyEvent.display), call_data); + break; + case OS.XK_Left: + xChange = -stepSize; + break; + case OS.XK_Right: + xChange = stepSize; + break; + case OS.XK_Up: + yChange = -stepSize; + break; + case OS.XK_Down: + yChange = stepSize; + break; + } + if (xChange != 0 || yChange != 0) { + Rectangle [] oldRectangles = rectangles; + boolean oldStippled = stippled; + Rectangle [] rectsToErase = new Rectangle [rectangles.length]; + for (int i = 0; i < rectangles.length; i++) { + Rectangle current = rectangles [i]; + rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height); + } + Event event = new Event (); + event.x = oldX + xChange; + event.y = oldY + yChange; + Point cursorPos; + if ((style & SWT.RESIZE) != 0) { + resizeRectangles (xChange, yChange); + sendEvent (SWT.Resize, event); + /* + * It is possible (but unlikely) that application code + * could have disposed the widget in the resize event. + * If this happens then return false to indicate that + * the move failed. + */ + if (isDisposed ()) { + cancelled = true; + return 1; + } + boolean draw = false; + /* + * It is possible that application code could have + * changed the rectangles in the resize event. If this + * happens then only redraw the tracker if the rectangle + * values have changed. + */ + if (rectangles != oldRectangles) { + int length = rectangles.length; + if (length != rectsToErase.length) { + draw = true; + } else { + for (int i = 0; i < length; i++) { + if (!rectangles [i].equals (rectsToErase [i])) { + draw = true; + break; + } + } + } + } else { + draw = true; + } + if (draw) { + drawRectangles (rectsToErase, oldStippled); + drawRectangles (rectangles, stippled); + } + cursorPos = adjustResizeCursor (); + } else { + moveRectangles (xChange, yChange); + sendEvent (SWT.Move, event); + /* + * It is possible (but unlikely) that application code + * could have disposed the widget in the move event. + * If this happens then return false to indicate that + * the move failed. + */ + if (isDisposed ()) { + cancelled = true; + return 1; + } + boolean draw = false; + /* + * It is possible that application code could have + * changed the rectangles in the move event. If this + * happens then only redraw the tracker if the rectangle + * values have changed. + */ + if (rectangles != oldRectangles) { + int length = rectangles.length; + if (length != rectsToErase.length) { + draw = true; + } else { + for (int i = 0; i < length; i++) { + if (!rectangles [i].equals (rectsToErase [i])) { + draw = true; + break; + } + } + } + } else { + draw = true; + } + if (draw) { + drawRectangles (rectsToErase, oldStippled); + drawRectangles (rectangles, stippled); + } + cursorPos = adjustMoveCursor (); + } + oldX = cursorPos.x; + oldY = cursorPos.y; + } + } + return result; +} + +int XPointerMotion (int w, int client_data, int call_data, int continue_to_dispatch) { + if (cursor != 0) { + int xDisplay = display.xDisplay; + OS.XChangeActivePointerGrab (xDisplay, + OS.ButtonPressMask | OS.ButtonReleaseMask | OS.PointerMotionMask, + cursor, OS.CurrentTime); + } + return xMouse (OS.MotionNotify, w, client_data, call_data, continue_to_dispatch); +} + +int xMouse (int type, int w, int client_data, int call_data, int continue_to_dispatch) { + int xDisplay = display.xDisplay; + int [] newX = new int [1], newY = new int [1], unused = new int [1]; + OS.XQueryPointer (xDisplay, window, unused, unused, newX, newY, unused, unused, unused); + if (oldX != newX [0] || oldY != newY [0]) { + Rectangle [] oldRectangles = rectangles; + boolean oldStippled = stippled; + Rectangle [] rectsToErase = new Rectangle [rectangles.length]; + for (int i = 0; i < rectangles.length; i++) { + Rectangle current = rectangles [i]; + rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height); + } + Event event = new Event (); + event.x = newX [0]; + event.y = newY [0]; + if ((style & SWT.RESIZE) != 0) { + resizeRectangles (newX [0] - oldX, newY [0] - oldY); + sendEvent (SWT.Resize, event); + /* + * It is possible (but unlikely) that application code + * could have disposed the widget in the resize event. + * If this happens then return false to indicate that + * the move failed. + */ + if (isDisposed ()) { + cancelled = true; + return 1; + } + boolean draw = false; + /* + * It is possible that application code could have + * changed the rectangles in the resize event. If this + * happens then only redraw the tracker if the rectangle + * values have changed. + */ + if (rectangles != oldRectangles) { + int length = rectangles.length; + if (length != rectsToErase.length) { + draw = true; + } else { + for (int i = 0; i < length; i++) { + if (!rectangles [i].equals (rectsToErase [i])) { + draw = true; + break; + } + } + } + } else { + draw = true; + } + if (draw) { + drawRectangles (rectsToErase, oldStippled); + drawRectangles (rectangles, stippled); + } + Point cursorPos = adjustResizeCursor (); + newX [0] = cursorPos.x; + newY [0] = cursorPos.y; + } else { + moveRectangles (newX [0] - oldX, newY [0] - oldY); + sendEvent (SWT.Move, event); + /* + * It is possible (but unlikely) that application code + * could have disposed the widget in the move event. + * If this happens then return false to indicate that + * the move failed. + */ + if (isDisposed ()) { + cancelled = true; + return 1; + } + boolean draw = false; + /* + * It is possible that application code could have + * changed the rectangles in the move event. If this + * happens then only redraw the tracker if the rectangle + * values have changed. + */ + if (rectangles != oldRectangles) { + int length = rectangles.length; + if (length != rectsToErase.length) { + draw = true; + } else { + for (int i = 0; i < length; i++) { + if (!rectangles [i].equals (rectsToErase [i])) { + draw = true; + break; + } + } + } + } else { + draw = true; + } + if (draw) { + drawRectangles (rectsToErase, oldStippled); + drawRectangles (rectangles, stippled); + } + } + oldX = newX [0]; + oldY = newY [0]; + } + tracking = type != OS.ButtonRelease; + return 0; } + +}
\ No newline at end of file diff --git a/bundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Widget.java b/bundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Widget.java index 0fae8e6fea..5d0134ccbf 100755 --- a/bundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Widget.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/motif/org/eclipse/swt/widgets/Widget.java @@ -353,6 +353,9 @@ char fixMnemonic (char [] buffer) { int focusProc (int w, int client_data, int call_data, int continue_to_dispatch) { return 0; } +String getCodePage () { + return null; +} /** * Returns the application defined widget data associated * with the receiver, or null if it has not been set. The @@ -754,6 +757,106 @@ void sendEvent (int eventType, Event event, boolean send) { display.postEvent (event); } } +boolean sendIMKeyEvent (int type, XKeyEvent xEvent) { + return sendIMKeyEvent (type, xEvent, 0); +} +boolean sendIMKeyEvent (int type, XKeyEvent xEvent, int textHandle) { + /* + * Bug in Motif. On Linux only, XmImMbLookupString () does not return + * XBufferOverflow as the status if the buffer is too small. The fix + * is to pass a large buffer. + */ + byte [] buffer = new byte [512]; + int [] status = new int [1], unused = new int [1]; + int focusHandle = OS.XtWindowToWidget (xEvent.display, xEvent.window); + int length = OS.XmImMbLookupString (focusHandle, xEvent, buffer, buffer.length, unused, status); + if (status [0] == OS.XBufferOverflow) { + buffer = new byte [length]; + length = OS.XmImMbLookupString (focusHandle, xEvent, buffer, length, unused, status); + } + if (length == 0) return true; + + /* Convert from MBCS to UNICODE and send the event */ + /* Use the character encoding for the default locale */ + char [] chars = Converter.mbcsToWcs (null, buffer); + int index = 0, count = 0; + while (index < chars.length) { + if (chars [index] == 0) { + chars [count] = 0; + break; + } + Event event = new Event (); + event.time = xEvent.time; + event.character = chars [index]; + setInputState (event, xEvent.state); + sendEvent (type, event); + // widget could be disposed at this point + + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the key + * events. If this happens, end the processing of + * the key by returning false. + */ + if (isDisposed ()) return false; + if (event.doit) chars [count++] = chars [index]; + index++; + } + if (count == 0) return false; + if (textHandle != 0) { + /* + * Bug in Motif. On Solaris and Linux, XmImMbLookupString() clears + * the characters from the IME. This causes the characters to be + * stolen from the text widget. The fix is to detect that the IME + * has been cleared and use XmTextInsert() to insert the stolen + * characters. This problem does not happen on AIX. + */ + byte [] testBuffer = new byte [5]; + int testLength = OS.XmImMbLookupString (textHandle, xEvent, testBuffer, testBuffer.length, unused, unused); + if (testLength == 0 || index != count) { + int [] start = new int [1], end = new int [1]; + OS.XmTextGetSelectionPosition (textHandle, start, end); + if (start [0] == end [0]) { + start [0] = end [0] = OS.XmTextGetInsertionPosition (textHandle); + } + boolean warnings = display.getWarnings (); + display.setWarnings (false); + if (index != count) { + buffer = Converter.wcsToMbcs (getCodePage (), chars, true); + } + OS.XmTextReplace (textHandle, start [0], end [0], buffer); + int position = start [0] + count; + OS.XmTextSetInsertionPosition (textHandle, position); + display.setWarnings (warnings); + return false; + } + } + return true; +} +boolean sendKeyEvent (int type, XKeyEvent xEvent) { + Event event = new Event (); + event.time = xEvent.time; + if (!setKeyState (event, xEvent)) return true; + Widget control = this; + if ((state & CANVAS) != 0) { + if ((style & SWT.NO_FOCUS) != 0) { + control = display.getFocusControl (); + } + } + if (control != null) { + control.sendEvent (type, event); + // widget could be disposed at this point + + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the key + * events. If this happens, end the processing of + * the key by returning false. + */ + if (isDisposed ()) return false; + } + return event.doit; +} /** * Sets the application defined widget data associated * with the receiver to be the argument. The <em>widget @@ -961,9 +1064,27 @@ int XFocusChange (int w, int client_data, int call_data, int continue_to_dispatc return 0; } int XKeyPress (int w, int client_data, int call_data, int continue_to_dispatch) { + XKeyEvent xEvent = new XKeyEvent (); + OS.memmove (xEvent, call_data, XKeyEvent.sizeof); + boolean doit = true; + if (xEvent.keycode != 0) { + doit = sendKeyEvent (SWT.KeyDown, xEvent); + } else { + doit = sendIMKeyEvent (SWT.KeyDown, xEvent); + } + if (!doit) { + OS.memmove (continue_to_dispatch, new int [1], 4); + return 1; + } return 0; } int XKeyRelease (int w, int client_data, int call_data, int continue_to_dispatch) { + XKeyEvent xEvent = new XKeyEvent (); + OS.memmove (xEvent, call_data, XKeyEvent.sizeof); + if (!sendKeyEvent (SWT.KeyUp, xEvent)) { + OS.memmove (continue_to_dispatch, new int [1], 4); + return 1; + } return 0; } int XLeaveWindow (int w, int client_data, int call_data, int continue_to_dispatch) { diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java index bda007b08c..201282607d 100755 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Control.java @@ -3082,20 +3082,7 @@ LRESULT WM_ACTIVATE (int wParam, int lParam) { } LRESULT WM_CHAR (int wParam, int lParam) { - /* - * Do not report a lead byte as a key pressed. - */ - if (!OS.IsUnicode && OS.IsDBLocale) { - byte lead = (byte) (wParam & 0xFF); - if (OS.IsDBCSLeadByte (lead)) return null; - } - display.lastAscii = wParam; - display.lastNull = wParam == 0; - if (!sendKeyEvent (SWT.KeyDown, OS.WM_CHAR, wParam, lParam)) { - return LRESULT.ONE; - } - // widget could be disposed at this point - return null; + return wmChar (handle, wParam, lParam); } LRESULT WM_CLEAR (int wParam, int lParam) { @@ -3281,17 +3268,7 @@ LRESULT WM_HSCROLL (int wParam, int lParam) { } LRESULT WM_IME_CHAR (int wParam, int lParam) { - Display display = this.display; - display.lastKey = 0; - display.lastAscii = wParam; - display.lastVirtual = display.lastNull = display.lastDead = false; - if (!sendKeyEvent (SWT.KeyDown, OS.WM_IME_CHAR, wParam, lParam)) { - return LRESULT.ONE; - } - sendKeyEvent (SWT.KeyUp, OS.WM_IME_CHAR, wParam, lParam); - // widget could be disposed at this point - display.lastKey = display.lastAscii = 0; - return LRESULT.ONE; + return wmIMEChar (handle, wParam, lParam); } LRESULT WM_IME_COMPOSITION (int wParam, int lParam) { @@ -3355,317 +3332,11 @@ LRESULT WM_INITMENUPOPUP (int wParam, int lParam) { } LRESULT WM_KEYDOWN (int wParam, int lParam) { - - /* Ignore repeating modifier keys by testing key down state */ - switch (wParam) { - case OS.VK_SHIFT: - case OS.VK_MENU: - case OS.VK_CONTROL: - case OS.VK_CAPITAL: - case OS.VK_NUMLOCK: - case OS.VK_SCROLL: - if ((lParam & 0x40000000) != 0) return null; - } - - /* Clear last key and last ascii because a new key has been typed */ - display.lastAscii = display.lastKey = 0; - display.lastVirtual = display.lastNull = display.lastDead = false; - - /* - * Do not report a lead byte as a key pressed. - */ - if (!OS.IsUnicode && OS.IsDBLocale) { - byte lead = (byte) (wParam & 0xFF); - if (OS.IsDBCSLeadByte (lead)) return null; - } - - /* Map the virtual key */ - /* - * Bug in WinCE. MapVirtualKey() returns incorrect values. - * The fix is to rely on a key mappings table to determine - * whether the key event must be sent now or if a WM_CHAR - * event will follow. The key mappings table maps virtual - * keys to SWT key codes and does not contain mappings for - * Windows virtual keys like VK_A. Virtual keys that are - * both virtual and ASCII are a special case. - */ - int mapKey = 0; - if (OS.IsWinCE) { - switch (wParam) { - case OS.VK_BACK: mapKey = SWT.BS; break; - case OS.VK_RETURN: mapKey = SWT.CR; break; - case OS.VK_DELETE: mapKey = SWT.DEL; break; - case OS.VK_ESCAPE: mapKey = SWT.ESC; break; - case OS.VK_TAB: mapKey = SWT.TAB; break; - } - } else { - mapKey = OS.MapVirtualKey (wParam, 2); - } - - /* - * Bug in Windows 95 and NT. When the user types an accent key such - * as ^ to get an accented character on a German keyboard, the accent - * key should be ignored and the next key that the user types is the - * accented key. The fix is to detect the accent key stroke (called - * a dead key) by testing the high bit of the value returned by - * MapVirtualKey(). A further problem is that the high bit on - * Windows NT is bit 32 while the high bit on Windows 95 is bit 16. - * They should both be bit 32. - * - * When the user types an accent key that does not correspond to a - * virtual key, MapVirtualKey() won't set the high bit to indicate - * a dead key. This happens when an accent key, such as '^' is the - * result of a modifier such as Shift key and MapVirtualKey() always - * returns the unshifted key. The fix is to peek for a WM_DEADCHAR - * and avoid issuing the event. - */ - if (OS.IsWinNT) { - if ((mapKey & 0x80000000) != 0) return null; - } else { - if ((mapKey & 0x8000) != 0) return null; - } - MSG msg = new MSG (); - int flags = OS.PM_NOREMOVE | OS.PM_NOYIELD | OS.PM_QS_INPUT | OS.PM_QS_POSTMESSAGE; - if (OS.PeekMessage (msg, handle, OS.WM_DEADCHAR, OS.WM_DEADCHAR, flags)) { - display.lastDead = true; - display.lastVirtual = mapKey == 0; - display.lastKey = display.lastVirtual ? wParam : mapKey; - return null; - } - - /* - * If we are going to get a WM_CHAR, ensure that last key has - * the correct character value for the key down and key up - * events. It is not sufficient to ignore the WM_KEYDOWN - * (when we know we are going to get a WM_CHAR) and compute - * the key in WM_CHAR because there is not enough information - * by the time we get the WM_CHAR. For example, when the user - * types Ctrl+Shift+6 on a US keyboard, we get a WM_CHAR with - * wParam=30. When the user types Ctrl+Shift+6 on a German - * keyboard, we also get a WM_CHAR with wParam=30. On the US - * keyboard Shift+6 is ^, on the German keyboard Shift+6 is &. - * There is no way to map wParam=30 in WM_CHAR to the correct - * value. Also, on international keyboards, the control key - * may be down when the user has not entered a control character. - * - * NOTE: On Windows 98, keypad keys are virtual despite the - * fact that a WM_CHAR is issued. On Windows 2000 and XP, - * they are not virtual. Therefore it is necessary to force - * numeric keypad keys to be virtual. - */ - display.lastVirtual = mapKey == 0 || display.numpadKey (wParam) != 0; - if (display.lastVirtual) { - display.lastKey = wParam; - /* - * Feature in Windows. The virtual key VK_DELETE is not - * treated as both a virtual key and an ASCII key by Windows. - * Therefore, we will not receive a WM_CHAR for this key. - * The fix is to treat VK_DELETE as a special case and map - * the ASCII value explictly (Delete is 0x7F). - */ - if (display.lastKey == OS.VK_DELETE) display.lastAscii = 0x7F; - - /* - * It is possible to get a WM_CHAR for a virtual key when - * Num Lock is on. If the user types Home while Num Lock - * is down, a WM_CHAR is issued with WPARM=55 (for the - * character 7). If we are going to get a WM_CHAR we need - * to ensure that the last key has the correct value. Note - * that Ctrl+Home does not issue a WM_CHAR when Num Lock is - * down. - */ - if (OS.VK_NUMPAD0 <= display.lastKey && display.lastKey <= OS.VK_DIVIDE) { - /* - * Feature in Windows. Calling to ToAscii() or ToUnicode(), clears - * the accented state such that the next WM_CHAR loses the accent. - * This makes is critical that the accent key is detected. Also, - * these functions clear the character that is entered using the - * special Windows keypad sequence when NumLock is down (ie. typing - * ALT+0231 should gives 'c' with a cedilla when NumLock is down). - */ - if (display.asciiKey (display.lastKey) != 0) return null; - display.lastAscii = display.numpadKey (display.lastKey); - } - } else { - /* - * Convert LastKey to lower case because Windows non-virtual - * keys that are also ASCII keys, such as like VK_A, are have - * upper case values in WM_KEYDOWN despite the fact that the - * Shift was not pressed. - */ - display.lastKey = OS.CharLower ((short) mapKey); - - /* - * Feature in Windows. The virtual key VK_CANCEL is treated - * as both a virtual key and ASCII key by Windows. This - * means that a WM_CHAR with WPARAM=3 will be issued for - * this key. In order to distinguish between this key and - * Ctrl+C, mark the key as virtual. - */ - if (wParam == OS.VK_CANCEL) display.lastVirtual = true; - - /* - * Some key combinations map to Windows ASCII keys depending - * on the keyboard. For example, Ctrl+Alt+Q maps to @ on a - * German keyboard. If the current key combination is special, - * the correct character is placed in wParam for processing in - * WM_CHAR. If this is the case, issue the key down event from - * inside WM_CHAR. - */ - int asciiKey = display.asciiKey (wParam); - if (asciiKey != 0) { - /* - * When the user types Ctrl+Space, ToAscii () maps this to - * Space. Normally, ToAscii () maps a key to a different - * key if both a WM_KEYDOWN and a WM_CHAR will be issued. - * To avoid the extra SWT.KeyDown, look for a space and - * issue the event from WM_CHAR. - */ - if (asciiKey == ' ') return null; - if (asciiKey != wParam) return null; - /* - * Feature in Windows. The virtual key VK_CANCEL is treated - * as both a virtual key and ASCII key by Windows. This - * means that a WM_CHAR with WPARAM=3 will be issued for - * this key. To avoid the extra SWT.KeyDown, look for - * VK_CANCEL and issue the event from WM_CHAR. - */ - if (wParam == OS.VK_CANCEL) return null; - } - - /* - * If the control key is not down at this point, then - * the key that was pressed was an accent key or a regular - * key such as 'A' or Shift+A. In that case, issue the - * key event from WM_CHAR. - */ - if (OS.GetKeyState (OS.VK_CONTROL) >= 0) return null; - - /* - * Get the shifted state or convert to lower case if necessary. - * If the user types Ctrl+A, LastAscii should be 'a', not 'A'. - * If the user types Ctrl+Shift+A, LastAscii should be 'A'. - * If the user types Ctrl+Shift+6, the value of LastAscii will - * depend on the international keyboard. - */ - if (OS.GetKeyState (OS.VK_SHIFT) < 0) { - display.lastAscii = display.shiftedKey (wParam); - if (display.lastAscii == 0) display.lastAscii = mapKey; - } else { - display.lastAscii = OS.CharLower ((short) mapKey); - } - - /* Note that Ctrl+'@' is ASCII NUL and is delivered in WM_CHAR */ - if (display.lastAscii == '@') return null; - display.lastAscii = display.controlKey (display.lastAscii); - } - if (!sendKeyEvent (SWT.KeyDown, OS.WM_KEYDOWN, wParam, lParam)) { - return LRESULT.ONE; - } - // widget could be disposed at this point - return null; + return wmKeyDown (handle, wParam, lParam); } LRESULT WM_KEYUP (int wParam, int lParam) { - Display display = this.display; - - /* Check for hardware keys */ - if (OS.IsWinCE) { - if (OS.VK_APP1 <= wParam && wParam <= OS.VK_APP6) { - display.lastKey = display.lastAscii = 0; - display.lastVirtual = display.lastNull = display.lastDead = false; - Event event = new Event (); - event.detail = wParam - OS.VK_APP1 + 1; - /* Check the bit 30 to get the key state */ - int type = (lParam & 0x40000000) != 0 ? SWT.HardKeyUp : SWT.HardKeyDown; - if (setInputState (event, type)) sendEvent (type, event); - // widget could be disposed at this point - return null; - } - } - - /* - * If the key up is not hooked, reset last key - * and last ascii in case the key down is hooked. - */ - if (!hooks (SWT.KeyUp) && !display.filters (SWT.KeyUp)) { - display.lastKey = display.lastAscii = 0; - display.lastVirtual = display.lastNull = display.lastDead = false; - return null; - } - - /* Map the virtual key. */ - /* - * Bug in WinCE. MapVirtualKey() returns incorrect values. - * The fix is to rely on a key mappings table to determine - * whether the key event must be sent now or if a WM_CHAR - * event will follow. The key mappings table maps virtual - * keys to SWT key codes and does not contain mappings for - * Windows virtual keys like VK_A. Virtual keys that are - * both virtual and ASCII are a special case. - */ - int mapKey = 0; - if (OS.IsWinCE) { - switch (wParam) { - case OS.VK_BACK: mapKey = SWT.BS; break; - case OS.VK_RETURN: mapKey = SWT.CR; break; - case OS.VK_DELETE: mapKey = SWT.DEL; break; - case OS.VK_ESCAPE: mapKey = SWT.ESC; break; - case OS.VK_TAB: mapKey = SWT.TAB; break; - } - } else { - mapKey = OS.MapVirtualKey (wParam, 2); - } - - /* - * Bug in Windows 95 and NT. When the user types an accent key such - * as ^ to get an accented character on a German keyboard, the accent - * key should be ignored and the next key that the user types is the - * accented key. The fix is to detect the accent key stroke (called - * a dead key) by testing the high bit of the value returned by - * MapVirtualKey (). A further problem is that the high bit on - * Windows NT is bit 32 while the high bit on Windows 95 is bit 16. - * They should both be bit 32. - */ - if (OS.IsWinNT) { - if ((mapKey & 0x80000000) != 0) return null; - } else { - if ((mapKey & 0x8000) != 0) return null; - } - if (display.lastDead) return null; - - /* - * NOTE: On Windows 98, keypad keys are virtual despite the - * fact that a WM_CHAR is issued. On Windows 2000 and XP, - * they are not virtual. Therefore it is necessary to force - * numeric keypad keys to be virtual. - */ - display.lastVirtual = mapKey == 0 || display.numpadKey (wParam) != 0; - if (display.lastVirtual) { - display.lastKey = wParam; - } else { - /* - * Feature in Windows. The virtual key VK_CANCEL is treated - * as both a virtual key and ASCII key by Windows. This - * means that a WM_CHAR with WPARAM=3 will be issued for - * this key. In order to distingush between this key and - * Ctrl+C, mark the key as virtual. - */ - if (wParam == OS.VK_CANCEL) display.lastVirtual = true; - if (display.lastKey == 0) { - display.lastAscii = 0; - display.lastNull = display.lastDead = false; - return null; - } - } - LRESULT result = null; - if (!sendKeyEvent (SWT.KeyUp, OS.WM_KEYUP, wParam, lParam)) { - result = LRESULT.ONE; - } - // widget could be disposed at this point - display.lastKey = display.lastAscii = 0; - display.lastVirtual = display.lastNull = display.lastDead = false; - return result; + return wmKeyUp (handle, wParam, lParam); } LRESULT WM_KILLFOCUS (int wParam, int lParam) { @@ -4237,27 +3908,7 @@ LRESULT WM_SIZE (int wParam, int lParam) { } LRESULT WM_SYSCHAR (int wParam, int lParam) { - Display display = this.display; - display.lastAscii = wParam; - display.lastNull = wParam == 0; - - /* Do not issue a key down if a menu bar mnemonic was invoked */ - if (!hooks (SWT.KeyDown) && !display.filters (SWT.KeyDown)) { - return null; - } - - /* Call the window proc to determine whether it is a system key or mnemonic */ - boolean oldKeyHit = display.mnemonicKeyHit; - display.mnemonicKeyHit = true; - int result = callWindowProc (OS.WM_SYSCHAR, wParam, lParam); - boolean consumed = false; - if (!display.mnemonicKeyHit) { - consumed = !sendKeyEvent (SWT.KeyDown, OS.WM_SYSCHAR, wParam, lParam); - // widget could be disposed at this point - } - consumed |= display.mnemonicKeyHit; - display.mnemonicKeyHit = oldKeyHit; - return consumed ? LRESULT.ONE : new LRESULT (result); + return wmSysChar (windowProc (), handle, wParam, lParam); } LRESULT WM_SYSCOLORCHANGE (int wParam, int lParam) { @@ -4378,106 +4029,11 @@ LRESULT WM_SYSCOMMAND (int wParam, int lParam) { } LRESULT WM_SYSKEYDOWN (int wParam, int lParam) { - /* - * Feature in Windows. When WM_SYSKEYDOWN is sent, - * the user pressed ALT+<key> or F10 to get to the - * menu bar. In order to issue events for F10 but - * ignore other key presses when the ALT is not down, - * make sure that either F10 was pressed or that ALT - * is pressed. - */ - if (wParam != OS.VK_F10) { - /* Make sure WM_SYSKEYDOWN was sent by ALT-<aKey>. */ - if ((lParam & 0x20000000) == 0) return null; - } - - /* Ignore well known system keys */ - switch (wParam) { - case OS.VK_F4: return null; - } - - /* Ignore repeating modifier keys by testing key down state */ - switch (wParam) { - case OS.VK_SHIFT: - case OS.VK_MENU: - case OS.VK_CONTROL: - case OS.VK_CAPITAL: - case OS.VK_NUMLOCK: - case OS.VK_SCROLL: - if ((lParam & 0x40000000) != 0) return null; - } - - /* Clear last key and last ascii because a new key has been typed */ - display.lastAscii = display.lastKey = 0; - display.lastVirtual = display.lastNull = display.lastDead = false; - - /* If are going to get a WM_SYSCHAR, ignore this message. */ - /* - * Bug in WinCE. MapVirtualKey() returns incorrect values. - * The fix is to rely on a key mappings table to determine - * whether the key event must be sent now or if a WM_CHAR - * event will follow. The key mappings table maps virtual - * keys to SWT key codes and does not contain mappings for - * Windows virtual keys like VK_A. Virtual keys that are - * both virtual and ASCII are a special case. - */ - int mapKey = 0; - if (OS.IsWinCE) { - switch (wParam) { - case OS.VK_BACK: mapKey = SWT.BS; break; - case OS.VK_RETURN: mapKey = SWT.CR; break; - case OS.VK_DELETE: mapKey = SWT.DEL; break; - case OS.VK_ESCAPE: mapKey = SWT.ESC; break; - case OS.VK_TAB: mapKey = SWT.TAB; break; - } - } else { - mapKey = OS.MapVirtualKey (wParam, 2); - } - display.lastVirtual = mapKey == 0 || display.numpadKey (wParam) != 0; - if (display.lastVirtual) { - display.lastKey = wParam; - /* - * Feature in Windows. The virtual key VK_DELETE is not - * treated as both a virtual key and an ASCII key by Windows. - * Therefore, we will not receive a WM_SYSCHAR for this key. - * The fix is to treat VK_DELETE as a special case and map - * the ASCII value explictly (Delete is 0x7F). - */ - if (display.lastKey == OS.VK_DELETE) display.lastAscii = 0x7F; - - /* When a keypad key is typed, a WM_SYSCHAR is not issued */ - if (OS.VK_NUMPAD0 <= display.lastKey && display.lastKey <= OS.VK_DIVIDE) { - display.lastAscii = display.numpadKey (display.lastKey); - } - } else { - /* - * Convert LastKey to lower case because Windows non-virtual - * keys that are also ASCII keys, such as like VK_A, are have - * upper case values in WM_SYSKEYDOWN despite the fact that the - * Shift was not pressed. - */ - display.lastKey = OS.CharLower ((short) mapKey); - - /* - * Feature in Windows 98. MapVirtualKey() indicates that - * a WM_SYSCHAR message will occur for Alt+Enter but - * this message never happens. The fix is to issue the - * event from WM_SYSKEYDOWN and map VK_RETURN to '\r'. - */ - if (OS.IsWinNT) return null; - if (wParam != OS.VK_RETURN) return null; - display.lastAscii = '\r'; - } - - if (!sendKeyEvent (SWT.KeyDown, OS.WM_SYSKEYDOWN, wParam, lParam)) { - return LRESULT.ONE; - } - // widget could be disposed at this point - return null; + return wmSysKeyDown (handle, wParam, lParam); } LRESULT WM_SYSKEYUP (int wParam, int lParam) { - return WM_KEYUP (wParam, lParam); + return wmSysKeyUp (handle, wParam, lParam); } LRESULT WM_TIMER (int wParam, int lParam) { diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tracker.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tracker.java index 22e5c51239..59797f28e0 100755 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tracker.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Tracker.java @@ -38,11 +38,12 @@ import org.eclipse.swt.events.*; */ public class Tracker extends Widget { Control parent; - boolean tracking, stippled; + boolean tracking, cancelled, stippled; Rectangle [] rectangles, proportions; Rectangle bounds; int resizeCursor, clientCursor, cursorOrientation = SWT.NONE; boolean inEvent = false; + int oldProc, oldX, oldY; /* * The following values mirror step sizes on Windows @@ -158,6 +159,33 @@ public void addControlListener (ControlListener listener) { addListener (SWT.Move, typedListener); } +/** + * Adds the listener to the collection of listeners who will + * be notified when keys are pressed and released on the system keyboard, by sending + * it one of the messages defined in the <code>KeyListener</code> + * interface. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see KeyListener + * @see #removeKeyListener + */ +public void addKeyListener (KeyListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + TypedListener typedListener = new TypedListener (listener); + addListener (SWT.KeyUp,typedListener); + addListener (SWT.KeyDown,typedListener); +} + Point adjustMoveCursor () { int newX = bounds.x + bounds.width / 2; int newY = bounds.y; @@ -413,11 +441,8 @@ void moveRectangles (int xChange, int yChange) { public boolean open () { checkWidget (); if (rectangles == null) return false; - boolean cancelled = false; + cancelled = false; tracking = true; - Event event = new Event (); - MSG msg = new MSG (); - boolean isMirrored = parent != null && (parent.style & SWT.MIRRORED) != 0; /* * If exactly one of UP/DOWN is specified as a style then set the cursor @@ -455,36 +480,8 @@ public boolean open () { 0, OS.GetModuleHandle (null), null); - final int oldProc = OS.GetWindowLong (hwndTransparent, OS.GWL_WNDPROC); - Object windowProc = new Object () { - public int windowProc (int hwnd, int msg, int wParam, int lParam) { - switch (msg) { - /* - * We typically do not want to answer that the transparent window is - * transparent to hits since doing so negates the effect of having it - * to grab events. However, clients of the tracker should not be aware - * of this transparent window. Therefore if there is a hit query - * performed as a result of client code then answer that the transparent - * window is transparent to hits so that its existence will not impact - * the client. - */ - case OS.WM_NCHITTEST: - if (inEvent) return OS.HTTRANSPARENT; - break; - case OS.WM_SETCURSOR: - if (clientCursor != 0) { - OS.SetCursor (clientCursor); - return 1; - } - if (resizeCursor != 0) { - OS.SetCursor (resizeCursor); - return 1; - } - } - return OS.CallWindowProc (oldProc, hwnd, msg, wParam, lParam); - } - }; - newProc = new Callback (windowProc, "windowProc", 4); //$NON-NLS-1$ + oldProc = OS.GetWindowLong (hwndTransparent, OS.GWL_WNDPROC); + newProc = new Callback (this, "transparentProc", 4); //$NON-NLS-1$ OS.SetWindowLong (hwndTransparent, OS.GWL_WNDPROC, newProc.getAddress ()); } @@ -501,255 +498,28 @@ public boolean open () { cursorPos = adjustMoveCursor (); } } - - int oldX = cursorPos.x, oldY = cursorPos.y; - /* - * Tracker behaves like a Dialog with its own OS event loop. - */ + oldX = cursorPos.x; + oldY = cursorPos.y; + + /* Tracker behaves like a Dialog with its own OS event loop. */ + MSG msg = new MSG (); while (tracking && !cancelled) { if (parent != null && parent.isDisposed ()) break; OS.GetMessage (msg, 0, 0, 0); + OS.TranslateMessage (msg); int message = msg.message; switch (message) { case OS.WM_LBUTTONUP: case OS.WM_MOUSEMOVE: - int newPos = OS.GetMessagePos (); - int newX = (short) (newPos & 0xFFFF); - int newY = (short) (newPos >> 16); - if (newX != oldX || newY != oldY) { - Rectangle [] oldRectangles = rectangles; - boolean oldStippled = stippled; - Rectangle [] rectsToErase = new Rectangle [rectangles.length]; - for (int i = 0; i < rectangles.length; i++) { - Rectangle current = rectangles [i]; - rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height); - } - event.x = newX; - event.y = newY; - if ((style & SWT.RESIZE) != 0) { - if (isMirrored) { - resizeRectangles (oldX - newX, newY - oldY); - } else { - resizeRectangles (newX - oldX, newY - oldY); - } - inEvent = true; - sendEvent (SWT.Resize, event); - inEvent = false; - /* - * It is possible (but unlikely), that application - * code could have disposed the widget in the resize - * event. If this happens, return false to indicate - * that the tracking has failed. - */ - if (isDisposed ()) { - cancelled = true; - break; - } - boolean draw = false; - /* - * It is possible that application code could have - * changed the rectangles in the resize event. If this - * happens then only redraw the tracker if the rectangle - * values have changed. - */ - if (rectangles != oldRectangles) { - int length = rectangles.length; - if (length != rectsToErase.length) { - draw = true; - } else { - for (int i = 0; i < length; i++) { - if (!rectangles [i].equals (rectsToErase [i])) { - draw = true; - break; - } - } - } - } - else { - draw = true; - } - if (draw) { - drawRectangles (rectsToErase, oldStippled); - drawRectangles (rectangles, stippled); - } - cursorPos = adjustResizeCursor (); - newX = cursorPos.x; newY = cursorPos.y; - } else { - if (isMirrored) { - moveRectangles (oldX - newX, newY - oldY); - } else { - moveRectangles (newX - oldX, newY - oldY); - } - inEvent = true; - sendEvent (SWT.Move, event); - inEvent = false; - /* - * It is possible (but unlikely), that application - * code could have disposed the widget in the move - * event. If this happens, return false to indicate - * that the tracking has failed. - */ - if (isDisposed ()) { - cancelled = true; - break; - } - boolean draw = false; - /* - * It is possible that application code could have - * changed the rectangles in the move event. If this - * happens then only redraw the tracker if the rectangle - * values have changed. - */ - if (rectangles != oldRectangles) { - int length = rectangles.length; - if (length != rectsToErase.length) { - draw = true; - } else { - for (int i = 0; i < length; i++) { - if (!rectangles [i].equals (rectsToErase [i])) { - draw = true; - break; - } - } - } - } else { - draw = true; - } - if (draw) { - drawRectangles (rectsToErase, oldStippled); - drawRectangles (rectangles, stippled); - } - } - oldX = newX; oldY = newY; - } - tracking = msg.message != OS.WM_LBUTTONUP; - break; - case OS.WM_SYSKEYDOWN: - cancelled = true; - tracking = false; - break; - case OS.WM_KEYDOWN: - int stepSize = OS.GetKeyState (OS.VK_CONTROL) < 0 ? STEPSIZE_SMALL : STEPSIZE_LARGE; - int xChange = 0, yChange = 0; - switch (msg.wParam) { - case OS.VK_ESCAPE: - cancelled = true; - tracking = false; - break; - case OS.VK_RETURN: - tracking = false; - break; - case OS.VK_LEFT: - xChange = isMirrored ? stepSize : -stepSize; - break; - case OS.VK_RIGHT: - xChange = isMirrored ? -stepSize : stepSize; - break; - case OS.VK_UP: - yChange = -stepSize; - break; - case OS.VK_DOWN: - yChange = stepSize; - break; - } - if (xChange != 0 || yChange != 0) { - Rectangle [] oldRectangles = rectangles; - boolean oldStippled = stippled; - Rectangle [] rectsToErase = new Rectangle [rectangles.length]; - for (int i = 0; i < rectangles.length; i++) { - Rectangle current = rectangles [i]; - rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height); - } - event.x = oldX + xChange; - event.y = oldY + yChange; - if ((style & SWT.RESIZE) != 0) { - resizeRectangles (xChange, yChange); - inEvent = true; - sendEvent (SWT.Resize, event); - inEvent = false; - /* - * It is possible (but unlikely) that application - * code could have disposed the widget in the resize - * event. If this happens return false to indicate - * that the tracking has failed. - */ - if (isDisposed ()) { - cancelled = true; - break; - } - boolean draw = false; - /* - * It is possible that application code could have - * changed the rectangles in the resize event. If this - * happens then only redraw the tracker if the rectangle - * values have changed. - */ - if (rectangles != oldRectangles) { - int length = rectangles.length; - if (length != rectsToErase.length) { - draw = true; - } else { - for (int i = 0; i < length; i++) { - if (!rectangles [i].equals (rectsToErase [i])) { - draw = true; - break; - } - } - } - } else { - draw = true; - } - if (draw) { - drawRectangles (rectsToErase, oldStippled); - drawRectangles (rectangles, stippled); - } - cursorPos = adjustResizeCursor (); - } else { - moveRectangles (xChange, yChange); - inEvent = true; - sendEvent (SWT.Move, event); - inEvent = false; - /* - * It is possible (but unlikely) that application - * code could have disposed the widget in the move - * event. If this happens return false to indicate - * that the tracking has failed. - */ - if (isDisposed ()) { - cancelled = true; - break; - } - boolean draw = false; - /* - * It is possible that application code could have - * changed the rectangles in the move event. If this - * happens then only redraw the tracker if the rectangle - * values have changed. - */ - if (rectangles != oldRectangles) { - int length = rectangles.length; - if (length != rectsToErase.length) { - draw = true; - } else { - for (int i = 0; i < length; i++) { - if (!rectangles [i].equals (rectsToErase [i])) { - draw = true; - break; - } - } - } - } else { - draw = true; - } - if (draw) { - drawRectangles (rectsToErase, oldStippled); - drawRectangles (rectangles, stippled); - } - cursorPos = adjustMoveCursor (); - } - oldX = cursorPos.x; oldY = cursorPos.y; - } + wmMouse (message, msg.wParam, msg.lParam); break; + case OS.WM_IME_CHAR: wmIMEChar (msg.hwnd, msg.wParam, msg.lParam); break; + case OS.WM_CHAR: wmChar (msg.hwnd, msg.wParam, msg.lParam); break; + case OS.WM_KEYDOWN: wmKeyDown (msg.hwnd, msg.wParam, msg.lParam); break; + case OS.WM_KEYUP: wmKeyUp (msg.hwnd, msg.wParam, msg.lParam); break; + case OS.WM_SYSCHAR: wmSysChar (0, msg.hwnd, msg.wParam, msg.lParam); break; + case OS.WM_SYSKEYDOWN: wmSysKeyDown (msg.hwnd, msg.wParam, msg.lParam); break; + case OS.WM_SYSKEYUP: wmSysKeyUp (msg.hwnd, msg.wParam, msg.lParam); break; } if (OS.WM_KEYFIRST <= message && message <= OS.WM_KEYLAST) continue; if (OS.WM_MOUSEFIRST <= message && message <= OS.WM_MOUSELAST) continue; @@ -766,6 +536,7 @@ public boolean open () { } if (newProc != null) { newProc.dispose (); + oldProc = 0; } /* * Cleanup: If this tracker was resizing then the last cursor that it created @@ -773,6 +544,7 @@ public boolean open () { */ if (resizeCursor != 0) { OS.DestroyCursor (resizeCursor); + resizeCursor = 0; } tracking = false; return !cancelled; @@ -803,6 +575,31 @@ public void removeControlListener (ControlListener listener) { eventTable.unhook (SWT.Move, listener); } +/** + * Removes the listener from the collection of listeners who will + * be notified when keys are pressed and released on the system keyboard. + * + * @param listener the listener which should be notified + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> + * </ul> + * @exception SWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> + * </ul> + * + * @see KeyListener + * @see #addKeyListener + */ +public void removeKeyListener(KeyListener listener) { + checkWidget (); + if (listener == null) error (SWT.ERROR_NULL_ARGUMENT); + if (eventTable == null) return; + eventTable.unhook (SWT.KeyUp, listener); + eventTable.unhook (SWT.KeyDown, listener); +} + void resizeRectangles (int xChange, int yChange) { /* * If the cursor orientation has not been set in the orientation of @@ -972,4 +769,287 @@ public void setStippled (boolean stippled) { checkWidget (); this.stippled = stippled; } + +int transparentProc (int hwnd, int msg, int wParam, int lParam) { + switch (msg) { + /* + * We typically do not want to answer that the transparent window is + * transparent to hits since doing so negates the effect of having it + * to grab events. However, clients of the tracker should not be aware + * of this transparent window. Therefore if there is a hit query + * performed as a result of client code then answer that the transparent + * window is transparent to hits so that its existence will not impact + * the client. + */ + case OS.WM_NCHITTEST: + if (inEvent) return OS.HTTRANSPARENT; + break; + case OS.WM_SETCURSOR: + if (clientCursor != 0) { + OS.SetCursor (clientCursor); + return 1; + } + if (resizeCursor != 0) { + OS.SetCursor (resizeCursor); + return 1; + } + } + return OS.CallWindowProc (oldProc, hwnd, msg, wParam, lParam); +} + +LRESULT wmKeyDown (int hwnd, int wParam, int lParam) { + LRESULT result = super.wmKeyDown (hwnd, wParam, lParam); + if (result != null) return result; + boolean isMirrored = parent != null && (parent.style & SWT.MIRRORED) != 0; + int stepSize = OS.GetKeyState (OS.VK_CONTROL) < 0 ? STEPSIZE_SMALL : STEPSIZE_LARGE; + int xChange = 0, yChange = 0; + switch (wParam) { + case OS.VK_ESCAPE: + cancelled = true; + tracking = false; + break; + case OS.VK_RETURN: + tracking = false; + break; + case OS.VK_LEFT: + xChange = isMirrored ? stepSize : -stepSize; + break; + case OS.VK_RIGHT: + xChange = isMirrored ? -stepSize : stepSize; + break; + case OS.VK_UP: + yChange = -stepSize; + break; + case OS.VK_DOWN: + yChange = stepSize; + break; + } + if (xChange != 0 || yChange != 0) { + Rectangle [] oldRectangles = rectangles; + boolean oldStippled = stippled; + Rectangle [] rectsToErase = new Rectangle [rectangles.length]; + for (int i = 0; i < rectangles.length; i++) { + Rectangle current = rectangles [i]; + rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height); + } + Event event = new Event (); + event.x = oldX + xChange; + event.y = oldY + yChange; + Point cursorPos; + if ((style & SWT.RESIZE) != 0) { + resizeRectangles (xChange, yChange); + inEvent = true; + sendEvent (SWT.Resize, event); + inEvent = false; + /* + * It is possible (but unlikely) that application + * code could have disposed the widget in the resize + * event. If this happens return false to indicate + * that the tracking has failed. + */ + if (isDisposed ()) { + cancelled = true; + return LRESULT.ONE; + } + boolean draw = false; + /* + * It is possible that application code could have + * changed the rectangles in the resize event. If this + * happens then only redraw the tracker if the rectangle + * values have changed. + */ + if (rectangles != oldRectangles) { + int length = rectangles.length; + if (length != rectsToErase.length) { + draw = true; + } else { + for (int i = 0; i < length; i++) { + if (!rectangles [i].equals (rectsToErase [i])) { + draw = true; + break; + } + } + } + } else { + draw = true; + } + if (draw) { + drawRectangles (rectsToErase, oldStippled); + drawRectangles (rectangles, stippled); + } + cursorPos = adjustResizeCursor (); + } else { + moveRectangles (xChange, yChange); + inEvent = true; + sendEvent (SWT.Move, event); + inEvent = false; + /* + * It is possible (but unlikely) that application + * code could have disposed the widget in the move + * event. If this happens return false to indicate + * that the tracking has failed. + */ + if (isDisposed ()) { + cancelled = true; + return LRESULT.ONE; + } + boolean draw = false; + /* + * It is possible that application code could have + * changed the rectangles in the move event. If this + * happens then only redraw the tracker if the rectangle + * values have changed. + */ + if (rectangles != oldRectangles) { + int length = rectangles.length; + if (length != rectsToErase.length) { + draw = true; + } else { + for (int i = 0; i < length; i++) { + if (!rectangles [i].equals (rectsToErase [i])) { + draw = true; + break; + } + } + } + } else { + draw = true; + } + if (draw) { + drawRectangles (rectsToErase, oldStippled); + drawRectangles (rectangles, stippled); + } + cursorPos = adjustMoveCursor (); + } + oldX = cursorPos.x; + oldY = cursorPos.y; + } + return result; +} + +LRESULT wmSysKeyDown (int hwnd, int wParam, int lParam) { + LRESULT result = super.wmSysKeyDown (hwnd, wParam, lParam); + if (result != null) return result; + cancelled = true; + tracking = false; + return result; +} + +LRESULT wmMouse (int message, int wParam, int lParam) { + boolean isMirrored = parent != null && (parent.style & SWT.MIRRORED) != 0; + int newPos = OS.GetMessagePos (); + int newX = (short) (newPos & 0xFFFF); + int newY = (short) (newPos >> 16); + if (newX != oldX || newY != oldY) { + Rectangle [] oldRectangles = rectangles; + boolean oldStippled = stippled; + Rectangle [] rectsToErase = new Rectangle [rectangles.length]; + for (int i = 0; i < rectangles.length; i++) { + Rectangle current = rectangles [i]; + rectsToErase [i] = new Rectangle (current.x, current.y, current.width, current.height); + } + Event event = new Event (); + event.x = newX; + event.y = newY; + if ((style & SWT.RESIZE) != 0) { + if (isMirrored) { + resizeRectangles (oldX - newX, newY - oldY); + } else { + resizeRectangles (newX - oldX, newY - oldY); + } + inEvent = true; + sendEvent (SWT.Resize, event); + inEvent = false; + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the resize + * event. If this happens, return false to indicate + * that the tracking has failed. + */ + if (isDisposed ()) { + cancelled = true; + return LRESULT.ONE; + } + boolean draw = false; + /* + * It is possible that application code could have + * changed the rectangles in the resize event. If this + * happens then only redraw the tracker if the rectangle + * values have changed. + */ + if (rectangles != oldRectangles) { + int length = rectangles.length; + if (length != rectsToErase.length) { + draw = true; + } else { + for (int i = 0; i < length; i++) { + if (!rectangles [i].equals (rectsToErase [i])) { + draw = true; + break; + } + } + } + } + else { + draw = true; + } + if (draw) { + drawRectangles (rectsToErase, oldStippled); + drawRectangles (rectangles, stippled); + } + Point cursorPos = adjustResizeCursor (); + newX = cursorPos.x; newY = cursorPos.y; + } else { + if (isMirrored) { + moveRectangles (oldX - newX, newY - oldY); + } else { + moveRectangles (newX - oldX, newY - oldY); + } + inEvent = true; + sendEvent (SWT.Move, event); + inEvent = false; + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the move + * event. If this happens, return false to indicate + * that the tracking has failed. + */ + if (isDisposed ()) { + cancelled = true; + return LRESULT.ONE; + } + boolean draw = false; + /* + * It is possible that application code could have + * changed the rectangles in the move event. If this + * happens then only redraw the tracker if the rectangle + * values have changed. + */ + if (rectangles != oldRectangles) { + int length = rectangles.length; + if (length != rectsToErase.length) { + draw = true; + } else { + for (int i = 0; i < length; i++) { + if (!rectangles [i].equals (rectsToErase [i])) { + draw = true; + break; + } + } + } + } else { + draw = true; + } + if (draw) { + drawRectangles (rectsToErase, oldStippled); + drawRectangles (rectangles, stippled); + } + } + oldX = newX; + oldY = newY; + } + tracking = message != OS.WM_LBUTTONUP; + return null; +} + } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Widget.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Widget.java index 07e9c3604f..d055f75ea3 100755 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Widget.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Widget.java @@ -166,6 +166,10 @@ public void addDisposeListener (DisposeListener listener) { addListener (SWT.Dispose, typedListener); } +int callWindowProc (int msg, int wParam, int lParam) { + return 0; +} + /** * Returns a style with exactly one style bit set out of * the specified set of exclusive style bits. All other @@ -823,6 +827,26 @@ void sendEvent (int eventType, Event event, boolean send) { } } +boolean sendKeyEvent (int type, int msg, int wParam, int lParam) { + Event event = new Event (); + if (!setKeyState (event, type, wParam, lParam)) return true; + return sendKeyEvent (type, msg, wParam, lParam, event); +} + +boolean sendKeyEvent (int type, int msg, int wParam, int lParam, Event event) { + sendEvent (type, event); + // widget could be disposed at this point + + /* + * It is possible (but unlikely), that application + * code could have disposed the widget in the key + * events. If this happens, end the processing of + * the key by returning false. + */ + if (isDisposed ()) return false; + return event.doit; +} + /** * Sets the application defined widget data associated * with the receiver to be the argument. The <em>widget @@ -1058,4 +1082,477 @@ public String toString () { } return getName () + " {" + string + "}"; //$NON-NLS-1$ //$NON-NLS-2$ } + +LRESULT wmChar (int hwnd, int wParam, int lParam) { + /* + * Do not report a lead byte as a key pressed. + */ + if (!OS.IsUnicode && OS.IsDBLocale) { + byte lead = (byte) (wParam & 0xFF); + if (OS.IsDBCSLeadByte (lead)) return null; + } + display.lastAscii = wParam; + display.lastNull = wParam == 0; + if (!sendKeyEvent (SWT.KeyDown, OS.WM_CHAR, wParam, lParam)) { + return LRESULT.ONE; + } + // widget could be disposed at this point + return null; +} + +LRESULT wmIMEChar (int hwnd, int wParam, int lParam) { + Display display = this.display; + display.lastKey = 0; + display.lastAscii = wParam; + display.lastVirtual = display.lastNull = display.lastDead = false; + if (!sendKeyEvent (SWT.KeyDown, OS.WM_IME_CHAR, wParam, lParam)) { + return LRESULT.ONE; + } + sendKeyEvent (SWT.KeyUp, OS.WM_IME_CHAR, wParam, lParam); + // widget could be disposed at this point + display.lastKey = display.lastAscii = 0; + return LRESULT.ONE; +} + +LRESULT wmKeyDown (int hwnd, int wParam, int lParam) { + + /* Ignore repeating modifier keys by testing key down state */ + switch (wParam) { + case OS.VK_SHIFT: + case OS.VK_MENU: + case OS.VK_CONTROL: + case OS.VK_CAPITAL: + case OS.VK_NUMLOCK: + case OS.VK_SCROLL: + if ((lParam & 0x40000000) != 0) return null; + } + + /* Clear last key and last ascii because a new key has been typed */ + display.lastAscii = display.lastKey = 0; + display.lastVirtual = display.lastNull = display.lastDead = false; + + /* + * Do not report a lead byte as a key pressed. + */ + if (!OS.IsUnicode && OS.IsDBLocale) { + byte lead = (byte) (wParam & 0xFF); + if (OS.IsDBCSLeadByte (lead)) return null; + } + + /* Map the virtual key */ + /* + * Bug in WinCE. MapVirtualKey() returns incorrect values. + * The fix is to rely on a key mappings table to determine + * whether the key event must be sent now or if a WM_CHAR + * event will follow. The key mappings table maps virtual + * keys to SWT key codes and does not contain mappings for + * Windows virtual keys like VK_A. Virtual keys that are + * both virtual and ASCII are a special case. + */ + int mapKey = 0; + if (OS.IsWinCE) { + switch (wParam) { + case OS.VK_BACK: mapKey = SWT.BS; break; + case OS.VK_RETURN: mapKey = SWT.CR; break; + case OS.VK_DELETE: mapKey = SWT.DEL; break; + case OS.VK_ESCAPE: mapKey = SWT.ESC; break; + case OS.VK_TAB: mapKey = SWT.TAB; break; + } + } else { + mapKey = OS.MapVirtualKey (wParam, 2); + } + + /* + * Bug in Windows 95 and NT. When the user types an accent key such + * as ^ to get an accented character on a German keyboard, the accent + * key should be ignored and the next key that the user types is the + * accented key. The fix is to detect the accent key stroke (called + * a dead key) by testing the high bit of the value returned by + * MapVirtualKey(). A further problem is that the high bit on + * Windows NT is bit 32 while the high bit on Windows 95 is bit 16. + * They should both be bit 32. + * + * When the user types an accent key that does not correspond to a + * virtual key, MapVirtualKey() won't set the high bit to indicate + * a dead key. This happens when an accent key, such as '^' is the + * result of a modifier such as Shift key and MapVirtualKey() always + * returns the unshifted key. The fix is to peek for a WM_DEADCHAR + * and avoid issuing the event. + */ + if (OS.IsWinNT) { + if ((mapKey & 0x80000000) != 0) return null; + } else { + if ((mapKey & 0x8000) != 0) return null; + } + MSG msg = new MSG (); + int flags = OS.PM_NOREMOVE | OS.PM_NOYIELD | OS.PM_QS_INPUT | OS.PM_QS_POSTMESSAGE; + if (OS.PeekMessage (msg, hwnd, OS.WM_DEADCHAR, OS.WM_DEADCHAR, flags)) { + display.lastDead = true; + display.lastVirtual = mapKey == 0; + display.lastKey = display.lastVirtual ? wParam : mapKey; + return null; + } + + /* + * If we are going to get a WM_CHAR, ensure that last key has + * the correct character value for the key down and key up + * events. It is not sufficient to ignore the WM_KEYDOWN + * (when we know we are going to get a WM_CHAR) and compute + * the key in WM_CHAR because there is not enough information + * by the time we get the WM_CHAR. For example, when the user + * types Ctrl+Shift+6 on a US keyboard, we get a WM_CHAR with + * wParam=30. When the user types Ctrl+Shift+6 on a German + * keyboard, we also get a WM_CHAR with wParam=30. On the US + * keyboard Shift+6 is ^, on the German keyboard Shift+6 is &. + * There is no way to map wParam=30 in WM_CHAR to the correct + * value. Also, on international keyboards, the control key + * may be down when the user has not entered a control character. + * + * NOTE: On Windows 98, keypad keys are virtual despite the + * fact that a WM_CHAR is issued. On Windows 2000 and XP, + * they are not virtual. Therefore it is necessary to force + * numeric keypad keys to be virtual. + */ + display.lastVirtual = mapKey == 0 || display.numpadKey (wParam) != 0; + if (display.lastVirtual) { + display.lastKey = wParam; + /* + * Feature in Windows. The virtual key VK_DELETE is not + * treated as both a virtual key and an ASCII key by Windows. + * Therefore, we will not receive a WM_CHAR for this key. + * The fix is to treat VK_DELETE as a special case and map + * the ASCII value explictly (Delete is 0x7F). + */ + if (display.lastKey == OS.VK_DELETE) display.lastAscii = 0x7F; + + /* + * It is possible to get a WM_CHAR for a virtual key when + * Num Lock is on. If the user types Home while Num Lock + * is down, a WM_CHAR is issued with WPARM=55 (for the + * character 7). If we are going to get a WM_CHAR we need + * to ensure that the last key has the correct value. Note + * that Ctrl+Home does not issue a WM_CHAR when Num Lock is + * down. + */ + if (OS.VK_NUMPAD0 <= display.lastKey && display.lastKey <= OS.VK_DIVIDE) { + /* + * Feature in Windows. Calling to ToAscii() or ToUnicode(), clears + * the accented state such that the next WM_CHAR loses the accent. + * This makes is critical that the accent key is detected. Also, + * these functions clear the character that is entered using the + * special Windows keypad sequence when NumLock is down (ie. typing + * ALT+0231 should gives 'c' with a cedilla when NumLock is down). + */ + if (display.asciiKey (display.lastKey) != 0) return null; + display.lastAscii = display.numpadKey (display.lastKey); + } + } else { + /* + * Convert LastKey to lower case because Windows non-virtual + * keys that are also ASCII keys, such as like VK_A, are have + * upper case values in WM_KEYDOWN despite the fact that the + * Shift was not pressed. + */ + display.lastKey = OS.CharLower ((short) mapKey); + + /* + * Feature in Windows. The virtual key VK_CANCEL is treated + * as both a virtual key and ASCII key by Windows. This + * means that a WM_CHAR with WPARAM=3 will be issued for + * this key. In order to distinguish between this key and + * Ctrl+C, mark the key as virtual. + */ + if (wParam == OS.VK_CANCEL) display.lastVirtual = true; + + /* + * Some key combinations map to Windows ASCII keys depending + * on the keyboard. For example, Ctrl+Alt+Q maps to @ on a + * German keyboard. If the current key combination is special, + * the correct character is placed in wParam for processing in + * WM_CHAR. If this is the case, issue the key down event from + * inside WM_CHAR. + */ + int asciiKey = display.asciiKey (wParam); + if (asciiKey != 0) { + /* + * When the user types Ctrl+Space, ToAscii () maps this to + * Space. Normally, ToAscii () maps a key to a different + * key if both a WM_KEYDOWN and a WM_CHAR will be issued. + * To avoid the extra SWT.KeyDown, look for a space and + * issue the event from WM_CHAR. + */ + if (asciiKey == ' ') return null; + if (asciiKey != wParam) return null; + /* + * Feature in Windows. The virtual key VK_CANCEL is treated + * as both a virtual key and ASCII key by Windows. This + * means that a WM_CHAR with WPARAM=3 will be issued for + * this key. To avoid the extra SWT.KeyDown, look for + * VK_CANCEL and issue the event from WM_CHAR. + */ + if (wParam == OS.VK_CANCEL) return null; + } + + /* + * If the control key is not down at this point, then + * the key that was pressed was an accent key or a regular + * key such as 'A' or Shift+A. In that case, issue the + * key event from WM_CHAR. + */ + if (OS.GetKeyState (OS.VK_CONTROL) >= 0) return null; + + /* + * Get the shifted state or convert to lower case if necessary. + * If the user types Ctrl+A, LastAscii should be 'a', not 'A'. + * If the user types Ctrl+Shift+A, LastAscii should be 'A'. + * If the user types Ctrl+Shift+6, the value of LastAscii will + * depend on the international keyboard. + */ + if (OS.GetKeyState (OS.VK_SHIFT) < 0) { + display.lastAscii = display.shiftedKey (wParam); + if (display.lastAscii == 0) display.lastAscii = mapKey; + } else { + display.lastAscii = OS.CharLower ((short) mapKey); + } + + /* Note that Ctrl+'@' is ASCII NUL and is delivered in WM_CHAR */ + if (display.lastAscii == '@') return null; + display.lastAscii = display.controlKey (display.lastAscii); + } + if (!sendKeyEvent (SWT.KeyDown, OS.WM_KEYDOWN, wParam, lParam)) { + return LRESULT.ONE; + } + // widget could be disposed at this point + return null; +} + +LRESULT wmKeyUp (int hwnd, int wParam, int lParam) { + Display display = this.display; + + /* Check for hardware keys */ + if (OS.IsWinCE) { + if (OS.VK_APP1 <= wParam && wParam <= OS.VK_APP6) { + display.lastKey = display.lastAscii = 0; + display.lastVirtual = display.lastNull = display.lastDead = false; + Event event = new Event (); + event.detail = wParam - OS.VK_APP1 + 1; + /* Check the bit 30 to get the key state */ + int type = (lParam & 0x40000000) != 0 ? SWT.HardKeyUp : SWT.HardKeyDown; + if (setInputState (event, type)) sendEvent (type, event); + // widget could be disposed at this point + return null; + } + } + + /* + * If the key up is not hooked, reset last key + * and last ascii in case the key down is hooked. + */ + if (!hooks (SWT.KeyUp) && !display.filters (SWT.KeyUp)) { + display.lastKey = display.lastAscii = 0; + display.lastVirtual = display.lastNull = display.lastDead = false; + return null; + } + + /* Map the virtual key. */ + /* + * Bug in WinCE. MapVirtualKey() returns incorrect values. + * The fix is to rely on a key mappings table to determine + * whether the key event must be sent now or if a WM_CHAR + * event will follow. The key mappings table maps virtual + * keys to SWT key codes and does not contain mappings for + * Windows virtual keys like VK_A. Virtual keys that are + * both virtual and ASCII are a special case. + */ + int mapKey = 0; + if (OS.IsWinCE) { + switch (wParam) { + case OS.VK_BACK: mapKey = SWT.BS; break; + case OS.VK_RETURN: mapKey = SWT.CR; break; + case OS.VK_DELETE: mapKey = SWT.DEL; break; + case OS.VK_ESCAPE: mapKey = SWT.ESC; break; + case OS.VK_TAB: mapKey = SWT.TAB; break; + } + } else { + mapKey = OS.MapVirtualKey (wParam, 2); + } + + /* + * Bug in Windows 95 and NT. When the user types an accent key such + * as ^ to get an accented character on a German keyboard, the accent + * key should be ignored and the next key that the user types is the + * accented key. The fix is to detect the accent key stroke (called + * a dead key) by testing the high bit of the value returned by + * MapVirtualKey (). A further problem is that the high bit on + * Windows NT is bit 32 while the high bit on Windows 95 is bit 16. + * They should both be bit 32. + */ + if (OS.IsWinNT) { + if ((mapKey & 0x80000000) != 0) return null; + } else { + if ((mapKey & 0x8000) != 0) return null; + } + if (display.lastDead) return null; + + /* + * NOTE: On Windows 98, keypad keys are virtual despite the + * fact that a WM_CHAR is issued. On Windows 2000 and XP, + * they are not virtual. Therefore it is necessary to force + * numeric keypad keys to be virtual. + */ + display.lastVirtual = mapKey == 0 || display.numpadKey (wParam) != 0; + if (display.lastVirtual) { + display.lastKey = wParam; + } else { + /* + * Feature in Windows. The virtual key VK_CANCEL is treated + * as both a virtual key and ASCII key by Windows. This + * means that a WM_CHAR with WPARAM=3 will be issued for + * this key. In order to distingush between this key and + * Ctrl+C, mark the key as virtual. + */ + if (wParam == OS.VK_CANCEL) display.lastVirtual = true; + if (display.lastKey == 0) { + display.lastAscii = 0; + display.lastNull = display.lastDead = false; + return null; + } + } + LRESULT result = null; + if (!sendKeyEvent (SWT.KeyUp, OS.WM_KEYUP, wParam, lParam)) { + result = LRESULT.ONE; + } + // widget could be disposed at this point + display.lastKey = display.lastAscii = 0; + display.lastVirtual = display.lastNull = display.lastDead = false; + return result; +} + +LRESULT wmSysChar (int windowProc, int hwnd, int wParam, int lParam) { + Display display = this.display; + display.lastAscii = wParam; + display.lastNull = wParam == 0; + + /* Do not issue a key down if a menu bar mnemonic was invoked */ + if (!hooks (SWT.KeyDown) && !display.filters (SWT.KeyDown)) { + return null; + } + + /* Call the window proc to determine whether it is a system key or mnemonic */ + boolean oldKeyHit = display.mnemonicKeyHit; + display.mnemonicKeyHit = true; + //TEMPORARY CODE + int result = callWindowProc (OS.WM_SYSCHAR, wParam, lParam); + boolean consumed = false; + if (!display.mnemonicKeyHit) { + consumed = !sendKeyEvent (SWT.KeyDown, OS.WM_SYSCHAR, wParam, lParam); + // widget could be disposed at this point + } + consumed |= display.mnemonicKeyHit; + display.mnemonicKeyHit = oldKeyHit; + return consumed ? LRESULT.ONE : new LRESULT (result); +} + +LRESULT wmSysKeyDown (int hwnd, int wParam, int lParam) { + /* + * Feature in Windows. When WM_SYSKEYDOWN is sent, + * the user pressed ALT+<key> or F10 to get to the + * menu bar. In order to issue events for F10 but + * ignore other key presses when the ALT is not down, + * make sure that either F10 was pressed or that ALT + * is pressed. + */ + if (wParam != OS.VK_F10) { + /* Make sure WM_SYSKEYDOWN was sent by ALT-<aKey>. */ + if ((lParam & 0x20000000) == 0) return null; + } + + /* Ignore well known system keys */ + switch (wParam) { + case OS.VK_F4: return null; + } + + /* Ignore repeating modifier keys by testing key down state */ + switch (wParam) { + case OS.VK_SHIFT: + case OS.VK_MENU: + case OS.VK_CONTROL: + case OS.VK_CAPITAL: + case OS.VK_NUMLOCK: + case OS.VK_SCROLL: + if ((lParam & 0x40000000) != 0) return null; + } + + /* Clear last key and last ascii because a new key has been typed */ + display.lastAscii = display.lastKey = 0; + display.lastVirtual = display.lastNull = display.lastDead = false; + + /* If are going to get a WM_SYSCHAR, ignore this message. */ + /* + * Bug in WinCE. MapVirtualKey() returns incorrect values. + * The fix is to rely on a key mappings table to determine + * whether the key event must be sent now or if a WM_CHAR + * event will follow. The key mappings table maps virtual + * keys to SWT key codes and does not contain mappings for + * Windows virtual keys like VK_A. Virtual keys that are + * both virtual and ASCII are a special case. + */ + int mapKey = 0; + if (OS.IsWinCE) { + switch (wParam) { + case OS.VK_BACK: mapKey = SWT.BS; break; + case OS.VK_RETURN: mapKey = SWT.CR; break; + case OS.VK_DELETE: mapKey = SWT.DEL; break; + case OS.VK_ESCAPE: mapKey = SWT.ESC; break; + case OS.VK_TAB: mapKey = SWT.TAB; break; + } + } else { + mapKey = OS.MapVirtualKey (wParam, 2); + } + display.lastVirtual = mapKey == 0 || display.numpadKey (wParam) != 0; + if (display.lastVirtual) { + display.lastKey = wParam; + /* + * Feature in Windows. The virtual key VK_DELETE is not + * treated as both a virtual key and an ASCII key by Windows. + * Therefore, we will not receive a WM_SYSCHAR for this key. + * The fix is to treat VK_DELETE as a special case and map + * the ASCII value explictly (Delete is 0x7F). + */ + if (display.lastKey == OS.VK_DELETE) display.lastAscii = 0x7F; + + /* When a keypad key is typed, a WM_SYSCHAR is not issued */ + if (OS.VK_NUMPAD0 <= display.lastKey && display.lastKey <= OS.VK_DIVIDE) { + display.lastAscii = display.numpadKey (display.lastKey); + } + } else { + /* + * Convert LastKey to lower case because Windows non-virtual + * keys that are also ASCII keys, such as like VK_A, are have + * upper case values in WM_SYSKEYDOWN despite the fact that the + * Shift was not pressed. + */ + display.lastKey = OS.CharLower ((short) mapKey); + + /* + * Feature in Windows 98. MapVirtualKey() indicates that + * a WM_SYSCHAR message will occur for Alt+Enter but + * this message never happens. The fix is to issue the + * event from WM_SYSKEYDOWN and map VK_RETURN to '\r'. + */ + if (OS.IsWinNT) return null; + if (wParam != OS.VK_RETURN) return null; + display.lastAscii = '\r'; + } + + if (!sendKeyEvent (SWT.KeyDown, OS.WM_SYSKEYDOWN, wParam, lParam)) { + return LRESULT.ONE; + } + // widget could be disposed at this point + return null; +} + +LRESULT wmSysKeyUp (int hwnd, int wParam, int lParam) { + return wmKeyUp (hwnd, wParam, lParam); +} } |