javafx.embed.swt.FXCanvas Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openjfx-78-backport Show documentation
Show all versions of openjfx-78-backport Show documentation
This is a backport of OpenJFX 8 to run on Java 7.
The newest version!
/*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javafx.embed.swt;
import java.lang.reflect.Field;
import java.nio.IntBuffer;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.sun.glass.ui.Application;
import com.sun.javafx.cursor.CursorFrame;
import com.sun.javafx.cursor.CursorType;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.input.TransferMode;
import com.sun.javafx.application.PlatformImpl;
import com.sun.javafx.embed.AbstractEvents;
import com.sun.javafx.embed.EmbeddedSceneDragSourceInterface;
import com.sun.javafx.embed.EmbeddedSceneDragStartListenerInterface;
import com.sun.javafx.embed.EmbeddedSceneDropTargetInterface;
import com.sun.javafx.embed.EmbeddedSceneInterface;
import com.sun.javafx.embed.EmbeddedStageInterface;
import com.sun.javafx.embed.HostInterface;
import com.sun.javafx.stage.EmbeddedWindow;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.HTMLTransfer;
import org.eclipse.swt.dnd.ImageTransfer;
import org.eclipse.swt.dnd.RTFTransfer;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.dnd.URLTransfer;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.MouseWheelListener;
/**
* {@code FXCanvas} is a component to embed JavaFX content into
* SWT applications. The content to be displayed is specified
* with the {@link #setScene} method that accepts an instance of
* JavaFX {@code Scene}. After the scene is assigned, it gets
* repainted automatically. All the input and focus events are
* forwarded to the scene transparently to the developer.
*
* Here is a typical pattern how {@code FXCanvas} can used:
*
* public class Test {
* private static Scene createScene() {
* Group group = new Group();
* Scene scene = new Scene(group);
* Button button = new Button("JFX Button");
* group.getChildren().add(button);
* return scene;
* }
*
* public static void main(String[] args) {
* Display display = new Display();
* Shell shell = new Shell(display);
* shell.setLayout(new FillLayout());
* FXCanvas canvas = new FXCanvas(shell, SWT.NONE);
* Scene scene = createScene();
* canvas.setScene(scene);
* shell.open();
* while (!shell.isDisposed()) {
* if (!display.readAndDispatch()) display.sleep();
* }
* display.dispose();
* }
* }
*
*
*
* @since JavaFX 2.0
*/
public class FXCanvas extends Canvas {
private HostContainer hostContainer;
private volatile EmbeddedWindow stage;
private volatile Scene scene;
private EmbeddedStageInterface stagePeer;
private EmbeddedSceneInterface scenePeer;
private int pWidth = 0;
private int pHeight = 0;
private volatile int pPreferredWidth = -1;
private volatile int pPreferredHeight = -1;
private IntBuffer pixelsBuf = null;
static Transfer [] StandardTransfers = new Transfer [] {
TextTransfer.getInstance(),
RTFTransfer.getInstance(),
HTMLTransfer.getInstance(),
URLTransfer.getInstance(),
ImageTransfer.getInstance(),
FileTransfer.getInstance(),
};
static Transfer [] CustomTransfers = new Transfer [0];
static Transfer [] getAllTransfers () {
Transfer [] transfers = new Transfer[StandardTransfers.length + CustomTransfers.length];
System.arraycopy(StandardTransfers, 0, transfers, 0, StandardTransfers.length);
System.arraycopy(CustomTransfers, 0, transfers, StandardTransfers.length, CustomTransfers.length);
return transfers;
}
static Transfer getCustomTransfer(String mime) {
for (int i=0; i() {
public Void run() {
System.setProperty("javafx.embed.isEventThread", "true");
return null;
}
});
Map map = Application.getDeviceDetails();
if (map == null) {
Application.setDeviceDetails(map = new HashMap());
}
if (map.get("javafx.embed.eventProc") == null) {
long eventProc = 0;
try {
Field field = Display.class.getDeclaredField("eventProc");
field.setAccessible(true);
if (field.getType() == int.class) {
eventProc = field.getLong(Display.getDefault());
} else {
if (field.getType() == long.class) {
eventProc = field.getLong(Display.getDefault());
}
}
} catch (Throwable th) {
//Fail silently
}
map.put("javafx.embed.eventProc", eventProc);
}
// Note that calling PlatformImpl.startup more than once is OK
PlatformImpl.startup(new Runnable() {
@Override
public void run() {
}
});
}
/**
* {@inheritDoc}
*/
public Point computeSize (int wHint, int hHint, boolean changed) {
checkWidget();
if (wHint == -1 && hHint == -1) {
if (pPreferredWidth != -1 && pPreferredHeight != -1) {
return new Point (pPreferredWidth, pPreferredHeight);
}
}
return super.computeSize(wHint, hHint, changed);
}
/**
* Returns the JavaFX scene attached to this {@code FXCanvas}.
*
* @return the {@code Scene} attached to this {@code FXCanvas}
*/
public Scene getScene() {
checkWidget();
return scene;
}
/**
* Attaches a {@code Scene} object to display in this {@code
* FXCanvas}. This method must called either on the JavaFX
* JavaFX application thread (which is the same as the SWT
* event dispatch thread).
*
* @param newScene a scene to display in this {@code FXCanvas}
*
* @see javafx.application.Platform#isFxApplicationThread()
*/
public void setScene(final Scene newScene) {
checkWidget();
if ((stage == null) && (newScene != null)) {
stage = new EmbeddedWindow(hostContainer);
stage.show();
}
scene = newScene;
if (stage != null) {
stage.setScene(newScene);
}
if ((stage != null) && (newScene == null)) {
stage.hide();
stage = null;
}
}
// Note that removing the listeners is unnecessary
private void registerEventListeners() {
addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent de) {
FXCanvas.this.widgetDisposed(de);
}
});
addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent pe) {
FXCanvas.this.paintControl(pe);
}
});
addMouseListener(new MouseListener() {
@Override
public void mouseDoubleClick(MouseEvent me) {
// Clicks and double-clicks are handled in FX
}
@Override
public void mouseDown(MouseEvent me) {
FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_PRESSED);
}
@Override
public void mouseUp(MouseEvent me) {
FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_RELEASED);
}
});
addMouseMoveListener(new MouseMoveListener() {
@Override
public void mouseMove(MouseEvent me) {
if ((me.stateMask & SWT.BUTTON_MASK) != 0) {
FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_DRAGGED);
} else {
FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_MOVED);
}
}
});
addMouseWheelListener(new MouseWheelListener() {
@Override
public void mouseScrolled(MouseEvent me) {
FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_WHEEL);
}
});
addMouseTrackListener(new MouseTrackListener() {
@Override
public void mouseEnter(MouseEvent me) {
FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_ENTERED);
}
@Override
public void mouseExit(MouseEvent me) {
FXCanvas.this.sendMouseEventToFX(me, AbstractEvents.MOUSEEVENT_EXITED);
}
@Override
public void mouseHover(MouseEvent me) {
// No mouse hovering in FX
}
});
addControlListener(new ControlListener() {
@Override
public void controlMoved(ControlEvent ce) {
FXCanvas.this.sendMoveEventToFX();
}
@Override
public void controlResized(ControlEvent ce) {
FXCanvas.this.sendResizeEventToFX();
}
});
addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent fe) {
FXCanvas.this.sendFocusEventToFX(fe, true);
}
@Override
public void focusLost(FocusEvent fe) {
FXCanvas.this.sendFocusEventToFX(fe, false);
}
});
addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent e) {
FXCanvas.this.sendKeyEventToFX(e, SWT.KeyDown);
}
@Override
public void keyReleased(KeyEvent e) {
FXCanvas.this.sendKeyEventToFX(e, SWT.KeyUp);
}
});
addMenuDetectListener(new MenuDetectListener() {
@Override
public void menuDetected(MenuDetectEvent e) {
FXCanvas.this.sendMenuEventToFX(e);
}
});
}
private void widgetDisposed(DisposeEvent de) {
if (stage != null) {
stage.hide();
}
}
int lastWidth, lastHeight;
IntBuffer lastPixelsBuf = null;
private void paintControl(PaintEvent pe) {
if ((scenePeer == null) || (pixelsBuf == null)) {
return;
}
// if we can't get the pixels, draw the bits that were there before
IntBuffer buffer = pixelsBuf;
int width = pWidth, height = pHeight;
if (scenePeer.getPixels(pixelsBuf, pWidth, pHeight)) {
width = lastWidth = pWidth;
height = lastHeight = pHeight;
buffer = lastPixelsBuf = pixelsBuf;
} else {
if (lastPixelsBuf == null) return;
width = lastWidth;
height = lastHeight;
buffer = lastPixelsBuf;
}
// Consider optimizing this
ImageData imageData = null;
if ("win32".equals(SWT.getPlatform())) {
PaletteData palette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000);
int scanline = width * 4;
byte[] dstData = new byte[scanline * height];
int[] srcData = buffer.array();
int dp = 0, sp = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int p = srcData[sp++];
dstData[dp++] = (byte) (p & 0xFF); //dst:blue
dstData[dp++] = (byte)((p >> 8) & 0xFF); //dst:green
dstData[dp++] = (byte)((p >> 16) & 0xFF); //dst:green
dstData[dp++] = (byte)0x00; //alpha
}
}
/*ImageData*/ imageData = new ImageData(width, height, 32, palette, 4, dstData);
} else {
PaletteData palette = new PaletteData(0x00ff0000, 0x0000ff00, 0x000000ff);
/*ImageData*/ imageData = new ImageData(width, height, 32, palette);
imageData.setPixels(0, 0,width * height, buffer.array(), 0);
}
Image image = new Image(Display.getDefault(), imageData);
pe.gc.drawImage(image, 0, 0);
image.dispose();
}
private void sendMoveEventToFX() {
if ((stagePeer == null) /*|| !isShowing()*/) {
return;
}
Rectangle rect = getClientArea();
Point los = toDisplay(rect.x, rect.y);
stagePeer.setLocation(los.x, los.y);
}
private void sendMouseEventToFX(MouseEvent me, int embedMouseType) {
if (scenePeer == null) {
return;
}
Point los = toDisplay(me.x, me.y);
boolean primaryBtnDown = (me.stateMask & SWT.BUTTON1) != 0;
boolean middleBtnDown = (me.stateMask & SWT.BUTTON2) != 0;
boolean secondaryBtnDown = (me.stateMask & SWT.BUTTON3) != 0;
boolean shift = (me.stateMask & SWT.SHIFT) != 0;
boolean control = (me.stateMask & SWT.CONTROL) != 0;
boolean alt = (me.stateMask & SWT.ALT) != 0;
boolean meta = (me.stateMask & SWT.COMMAND) != 0;
switch (embedMouseType) {
case AbstractEvents.MOUSEEVENT_PRESSED:
primaryBtnDown |= me.button == 1;
middleBtnDown |= me.button == 2;
secondaryBtnDown |= me.button == 3;
break;
case AbstractEvents.MOUSEEVENT_RELEASED:
primaryBtnDown &= me.button != 1;
middleBtnDown &= me.button != 2;
secondaryBtnDown &= me.button != 3;
break;
default:
break;
}
scenePeer.mouseEvent(
embedMouseType,
SWTEvents.mouseButtonToEmbedMouseButton(me.button, me.stateMask),
primaryBtnDown, middleBtnDown, secondaryBtnDown,
me.count,
me.x, me.y,
los.x, los.y,
shift, control, alt, meta,
SWTEvents.getWheelRotation(me, embedMouseType), false); // TODO: popup trigger
}
private void sendKeyEventToFX(final KeyEvent e, int type) {
if (scenePeer == null /*|| !isFxEnabled()*/) {
return;
}
int stateMask = e.stateMask;
if (type == SWT.KeyDown) {
if (e.keyCode == SWT.SHIFT) stateMask |= SWT.SHIFT;
if (e.keyCode == SWT.CONTROL) stateMask |= SWT.CONTROL;
if (e.keyCode == SWT.ALT) stateMask |= SWT.ALT;
if (e.keyCode == SWT.COMMAND) stateMask |= SWT.COMMAND;
} else {
if (e.keyCode == SWT.SHIFT) stateMask &= ~SWT.SHIFT;
if (e.keyCode == SWT.CONTROL) stateMask &= ~SWT.CONTROL;
if (e.keyCode == SWT.ALT) stateMask &= ~SWT.ALT;
if (e.keyCode == SWT.COMMAND) stateMask &= ~SWT.COMMAND;
}
int keyCode = SWTEvents.keyCodeToEmbedKeyCode(e.keyCode);
scenePeer.keyEvent(
SWTEvents.keyIDToEmbedKeyType(type),
keyCode, new char[0],
SWTEvents.keyModifiersToEmbedKeyModifiers(stateMask));
if (e.character != '\0' && type == SWT.KeyDown) {
char[] chars = new char[] { e.character };
scenePeer.keyEvent(
AbstractEvents.KEYEVENT_TYPED,
e.keyCode, chars,
SWTEvents.keyModifiersToEmbedKeyModifiers(stateMask));
}
}
private void sendMenuEventToFX(MenuDetectEvent me) {
if (scenePeer == null /*|| !isFxEnabled()*/) {
return;
}
Point pt = toControl(me.x, me.y);
scenePeer.menuEvent(pt.x, pt.y, me.x, me.y, false);
}
private void sendResizeEventToFX() {
// force the panel to draw right away (avoid black rectangle)
redraw();
update();
pWidth = getClientArea().width;
pHeight = getClientArea().height;
if ((pWidth <= 0) || (pHeight <= 0)) {
pixelsBuf = lastPixelsBuf = null;
} else {
pixelsBuf = IntBuffer.allocate(pWidth * pHeight);
}
if (scenePeer == null) {
return;
}
stagePeer.setSize(pWidth, pHeight);
scenePeer.setSize(pWidth, pHeight);
}
private void sendFocusEventToFX(FocusEvent fe, boolean focused) {
if ((stage == null) || (stagePeer == null)) {
return;
}
int focusCause = (focused ?
AbstractEvents.FOCUSEVENT_ACTIVATED :
AbstractEvents.FOCUSEVENT_DEACTIVATED);
stagePeer.setFocused(focused, focusCause);
}
private class HostContainer implements HostInterface {
@Override
public void setEmbeddedStage(EmbeddedStageInterface embeddedStage) {
stagePeer = embeddedStage;
if (stagePeer == null) {
return;
}
if (pWidth > 0 && pHeight > 0) {
stagePeer.setSize(pWidth, pHeight);
}
if (FXCanvas.this.isFocusControl()) {
stagePeer.setFocused(true, AbstractEvents.FOCUSEVENT_ACTIVATED);
}
sendMoveEventToFX();
sendResizeEventToFX();
}
TransferMode getTransferMode(int bits) {
switch (bits) {
case DND.DROP_COPY:
return TransferMode.COPY;
case DND.DROP_MOVE:
case DND.DROP_TARGET_MOVE:
return TransferMode.MOVE;
case DND.DROP_LINK:
return TransferMode.LINK;
default:
return null;
}
}
Set getTransferModes(int bits) {
Set set = new HashSet();
if ((bits & DND.DROP_COPY) != 0) set.add(TransferMode.COPY);
if ((bits & DND.DROP_MOVE) != 0) set.add(TransferMode.MOVE);
if ((bits & DND.DROP_TARGET_MOVE) != 0) set.add(TransferMode.MOVE);
if ((bits & DND.DROP_LINK) != 0) set.add(TransferMode.LINK);
return set;
}
// Consider using dragAction
private DragSource createDragSource(final EmbeddedSceneDragSourceInterface fxDragSource, TransferMode dragAction) {
Transfer [] transfers = getTransferTypes(fxDragSource.getMimeTypes());
if (transfers.length == 0) return null;
int dragOperation = getDragActions(fxDragSource.getSupportedActions());
final DragSource dragSource = new DragSource(FXCanvas.this, dragOperation);
dragSource.setTransfer(transfers);
dragSource.addDragListener(new DragSourceListener() {
public void dragFinished(org.eclipse.swt.dnd.DragSourceEvent event) {
dragSource.dispose();
fxDragSource.dragDropEnd(getTransferMode(event.detail));
}
public void dragSetData(org.eclipse.swt.dnd.DragSourceEvent event) {
Transfer [] transfers = dragSource.getTransfer();
for (int i=0; i set) {
int result = 0;
for (TransferMode mode : set) {
result |= getDragAction(mode);
}
return result;
}
Transfer getTransferType(String mime) {
if (mime.equals("text/plain")) return TextTransfer.getInstance();
if (mime.equals("text/rtf")) return RTFTransfer.getInstance();
if (mime.equals("text/html")) return HTMLTransfer.getInstance();
if (mime.equals("text/uri-list")) return URLTransfer.getInstance();
if (mime.equals("application/x-java-rawimage")) return ImageTransfer.getInstance();
if (mime.equals("application/x-java-file-list") || mime.equals("java.file-list")) {
return FileTransfer.getInstance();
}
return getCustomTransfer(mime);
}
Transfer [] getTransferTypes(String [] mimeTypes) {
int count= 0;
Transfer [] transfers = new Transfer [mimeTypes.length];
for (int i=0; i getSupportedActions() {
return getTransferModes(operations);
}
public Object getData(String mimeType) {
// NOTE: get the data for the requested mime type, not the default data
return data;
}
public String[] getMimeTypes() {
if (currentTransferData == null) return new String [0];
return getMimes(getAllTransfers(), currentTransferData);
}
public boolean isMimeTypeAvailable(String mimeType) {
String [] mimes = getMimeTypes();
for (int i=0; i 0 && pHeight > 0) {
scenePeer.setSize(pWidth, pHeight);
}
scenePeer.setDragStartListener(new EmbeddedSceneDragStartListenerInterface() {
@Override
public void dragStarted(final EmbeddedSceneDragSourceInterface fxDragSource, final TransferMode dragAction) {
Platform.runLater(new Runnable () {
public void run () {
DragSource dragSource = createDragSource(fxDragSource, dragAction);
if (dragSource == null) {
fxDragSource.dragDropEnd(null);
} else {
if (dropTarget != null) dropTarget.setTransfer(getAllTransfers());
FXCanvas.this.notifyListeners(SWT.DragDetect, null);
}
}
});
}
});
if (dropTarget != null) dropTarget.dispose();
dropTarget = createDropTarget(embeddedScene);
}
@Override
public boolean requestFocus() {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
if (isDisposed()) return;
FXCanvas.this.forceFocus();
}
});
return true;
}
@Override
public boolean traverseFocusOut(boolean bln) {
// RT-18085: not implemented
return true;
}
Object lock = new Object();
boolean queued = false;
public void repaint() {
synchronized (lock) {
if (queued) return;
queued = true;
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
try {
if (isDisposed()) return;
FXCanvas.this.redraw();
FXCanvas.this.sendMoveEventToFX();
} finally {
synchronized (lock) {
queued = false;
}
}
}
});
}
}
@Override
public void setPreferredSize(int width, int height) {
FXCanvas.this.pPreferredWidth = width;
FXCanvas.this.pPreferredHeight = height;
//FXCanvas.this.getShell().layout(new Control []{FXCanvas.this}, SWT.DEFER);
}
@Override
public void setEnabled(boolean bln) {
FXCanvas.this.setEnabled(bln);
}
@Override
public void setCursor(CursorFrame cursorFrame) {
FXCanvas.this.setCursor(getPlatformCursor(cursorFrame));
}
private org.eclipse.swt.graphics.Cursor getPlatformCursor(final CursorFrame cursorFrame) {
/*
* On the Mac, setting the cursor during drag and drop clears the move
* and link indicators. The fix is to set the default cursor for the
* control (which is null) when the FX explicitly requests the default
* cursor. This will preserve the drag and drop indicators.
*/
if (cursorFrame.getCursorType() == CursorType.DEFAULT) {
return null;
}
final org.eclipse.swt.graphics.Cursor cachedPlatformCursor =
cursorFrame.getPlatformCursor(org.eclipse.swt.graphics.Cursor.class);
if (cachedPlatformCursor != null) {
// platform cursor already cached
return cachedPlatformCursor;
}
// platform cursor not cached yet
final org.eclipse.swt.graphics.Cursor platformCursor =
SWTCursors.embedCursorToCursor(cursorFrame);
cursorFrame.setPlatforCursor(org.eclipse.swt.graphics.Cursor.class, platformCursor);
return platformCursor;
}
@Override
public boolean grabFocus() {
// RT-27949: not implemented
return true;
}
@Override
public void ungrabFocus() {
// RT-27949: not implemented
}
}
}