/* * Copyright (c) 1997, 2008, 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 sun.awt.im;import java.awt.AWTEvent;import java.awt.Color;import java.awt.Dimension;import java.awt.FontMetrics;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Point;import java.awt.Rectangle;import java.awt.Toolkit;import java.awt.event.InputMethodEvent;import java.awt.event.InputMethodListener;import java.awt.event.WindowEvent;import java.awt.event.WindowAdapter;import java.awt.font.FontRenderContext;import java.awt.font.TextHitInfo;import java.awt.font.TextLayout;import java.awt.geom.Rectangle2D;import java.awt.im.InputMethodRequests;import java.text.AttributedCharacterIterator;import javax.swing.JFrame;import javax.swing.JPanel;import javax.swing.border.LineBorder;/** * A composition area is used to display text that's being composed * using an input method in its own user interface environment, * typically in a root window. * * @author JavaSoft International */// This class is final due to the 6607310 fix. Refer to the CR for details.public final class CompositionArea extends JPanel implements InputMethodListener { private CompositionAreaHandler handler; private TextLayout composedTextLayout; private TextHitInfo caret = null; private JFrame compositionWindow; private final static int TEXT_ORIGIN_X = 5; private final static int TEXT_ORIGIN_Y = 15; private final static int PASSIVE_WIDTH = 480; private final static int WIDTH_MARGIN=10; private final static int HEIGHT_MARGIN=3; CompositionArea() { // create composition window with localized title String windowTitle = Toolkit.getProperty("AWT.CompositionWindowTitle", "Input Window"); compositionWindow = (JFrame)InputMethodContext.createInputMethodWindow(windowTitle, null, true); setOpaque(true); setBorder(LineBorder.createGrayLineBorder()); setForeground(Color.black); setBackground(Color.white); // if we get the focus, we still want to let the client's // input context handle the event enableInputMethods(true); enableEvents(AWTEvent.KEY_EVENT_MASK); compositionWindow.getContentPane().add(this); compositionWindow.addWindowListener(new FrameWindowAdapter()); addInputMethodListener(this); compositionWindow.enableInputMethods(false); compositionWindow.pack(); Dimension windowSize = compositionWindow.getSize(); Dimension screenSize = (getToolkit()).getScreenSize(); compositionWindow.setLocation(screenSize.width - windowSize.width-20, screenSize.height - windowSize.height-100); compositionWindow.setVisible(false); } /** * Sets the composition area handler that currently owns this * composition area, and its input context. */ synchronized void setHandlerInfo(CompositionAreaHandler handler, InputContext inputContext) { this.handler = handler; ((InputMethodWindow) compositionWindow).setInputContext(inputContext); } /** * @see java.awt.Component#getInputMethodRequests */ public InputMethodRequests getInputMethodRequests() { return handler; } // returns a 0-width rectangle private Rectangle getCaretRectangle(TextHitInfo caret) { int caretLocation = 0; TextLayout layout = composedTextLayout; if (layout != null) { caretLocation = Math.round(layout.getCaretInfo(caret)[0]); } Graphics g = getGraphics(); FontMetrics metrics = null; try { metrics = g.getFontMetrics(); } finally { g.dispose(); } return new Rectangle(TEXT_ORIGIN_X + caretLocation, TEXT_ORIGIN_Y - metrics.getAscent(), 0, metrics.getAscent() + metrics.getDescent()); } public void paint(Graphics g) { super.paint(g); g.setColor(getForeground()); TextLayout layout = composedTextLayout; if (layout != null) { layout.draw((Graphics2D) g, TEXT_ORIGIN_X, TEXT_ORIGIN_Y); } if (caret != null) { Rectangle rectangle = getCaretRectangle(caret); g.setXORMode(getBackground()); g.fillRect(rectangle.x, rectangle.y, 1, rectangle.height); g.setPaintMode(); } } // shows/hides the composition window void setCompositionAreaVisible(boolean visible) { compositionWindow.setVisible(visible); } // returns true if composition area is visible boolean isCompositionAreaVisible() { return compositionWindow.isVisible(); } // workaround for the Solaris focus lost problem class FrameWindowAdapter extends WindowAdapter { public void windowActivated(WindowEvent e) { requestFocus(); } } // InputMethodListener methods - just forward to the current handler public void inputMethodTextChanged(InputMethodEvent event) { handler.inputMethodTextChanged(event); } public void caretPositionChanged(InputMethodEvent event) { handler.caretPositionChanged(event); } /** * Sets the text and caret to be displayed in this composition area. * Shows the window if it contains text, hides it if not. */ void setText(AttributedCharacterIterator composedText, TextHitInfo caret) { composedTextLayout = null; if (composedText == null) { // there's no composed text to display, so hide the window compositionWindow.setVisible(false); this.caret = null; } else { /* since we have composed text, make sure the window is shown. This is necessary to get a valid graphics object. See 6181385. */ if (!compositionWindow.isVisible()) { compositionWindow.setVisible(true); } Graphics g = getGraphics(); if (g == null) { return; } try { updateWindowLocation(); FontRenderContext context = ((Graphics2D)g).getFontRenderContext(); composedTextLayout = new TextLayout(composedText, context); Rectangle2D bounds = composedTextLayout.getBounds(); this.caret = caret; // Resize the composition area to just fit the text. FontMetrics metrics = g.getFontMetrics(); Rectangle2D maxCharBoundsRec = metrics.getMaxCharBounds(g); int newHeight = (int)maxCharBoundsRec.getHeight() + HEIGHT_MARGIN; int newFrameHeight = newHeight +compositionWindow.getInsets().top +compositionWindow.getInsets().bottom; // If it's a passive client, set the width always to PASSIVE_WIDTH (480px) InputMethodRequests req = handler.getClientInputMethodRequests(); int newWidth = (req==null) ? PASSIVE_WIDTH : (int)bounds.getWidth() + WIDTH_MARGIN; int newFrameWidth = newWidth + compositionWindow.getInsets().left + compositionWindow.getInsets().right; setPreferredSize(new Dimension(newWidth, newHeight)); compositionWindow.setSize(new Dimension(newFrameWidth, newFrameHeight)); // show the composed text paint(g); } finally { g.dispose(); } } } /** * Sets the caret to be displayed in this composition area. * The text is not changed. */ void setCaret(TextHitInfo caret) { this.caret = caret; if (compositionWindow.isVisible()) { Graphics g = getGraphics(); try { paint(g); } finally { g.dispose(); } } } /** * Positions the composition window near (usually below) the * insertion point in the client component if the client * component is an active client (below-the-spot input). */ void updateWindowLocation() { InputMethodRequests req = handler.getClientInputMethodRequests(); if (req == null) { // not an active client return; } Point windowLocation = new Point(); Rectangle caretRect = req.getTextLocation(null); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); Dimension windowSize = compositionWindow.getSize(); final int SPACING = 2; if (caretRect.x + windowSize.width > screenSize.width) { windowLocation.x = screenSize.width - windowSize.width; } else { windowLocation.x = caretRect.x; } if (caretRect.y + caretRect.height + SPACING + windowSize.height > screenSize.height) { windowLocation.y = caretRect.y - SPACING - windowSize.height; } else { windowLocation.y = caretRect.y + caretRect.height + SPACING; } compositionWindow.setLocation(windowLocation); } // support for InputMethodRequests methods Rectangle getTextLocation(TextHitInfo offset) { Rectangle rectangle = getCaretRectangle(offset); Point location = getLocationOnScreen(); rectangle.translate(location.x, location.y); return rectangle; } TextHitInfo getLocationOffset(int x, int y) { TextLayout layout = composedTextLayout; if (layout == null) { return null; } else { Point location = getLocationOnScreen(); x -= location.x + TEXT_ORIGIN_X; y -= location.y + TEXT_ORIGIN_Y; if (layout.getBounds().contains(x, y)) { return layout.hitTestChar(x, y); } else { return null; } } } // Disables or enables decorations of the composition window void setCompositionAreaUndecorated(boolean setUndecorated){ if (compositionWindow.isDisplayable()){ compositionWindow.removeNotify(); } compositionWindow.setUndecorated(setUndecorated); compositionWindow.pack(); }}
/*
* Copyright (c) 1997, 2008, 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 sun.awt.im;
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.InputMethodEvent;
import java.awt.event.InputMethodListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import java.awt.font.FontRenderContext;
import java.awt.font.TextHitInfo;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.awt.im.InputMethodRequests;
import java.text.AttributedCharacterIterator;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
/**
* A composition area is used to display text that's being composed
* using an input method in its own user interface environment,
* typically in a root window.
*
* @author JavaSoft International
*/
// This class is final due to the 6607310 fix. Refer to the CR for details.
public final class CompositionArea extends JPanel implements InputMethodListener {
private CompositionAreaHandler handler;
private TextLayout composedTextLayout;
private TextHitInfo caret = null;
private JFrame compositionWindow;
private final static int TEXT_ORIGIN_X = 5;
private final static int TEXT_ORIGIN_Y = 15;
private final static int PASSIVE_WIDTH = 480;
private final static int WIDTH_MARGIN=10;
private final static int HEIGHT_MARGIN=3;
CompositionArea() {
// create composition window with localized title
String windowTitle = Toolkit.getProperty("AWT.CompositionWindowTitle", "Input Window");
compositionWindow =
(JFrame)InputMethodContext.createInputMethodWindow(windowTitle, null, true);
setOpaque(true);
setBorder(LineBorder.createGrayLineBorder());
setForeground(Color.black);
setBackground(Color.white);
// if we get the focus, we still want to let the client's
// input context handle the event
enableInputMethods(true);
enableEvents(AWTEvent.KEY_EVENT_MASK);
compositionWindow.getContentPane().add(this);
compositionWindow.addWindowListener(new FrameWindowAdapter());
addInputMethodListener(this);
compositionWindow.enableInputMethods(false);
compositionWindow.pack();
Dimension windowSize = compositionWindow.getSize();
Dimension screenSize = (getToolkit()).getScreenSize();
compositionWindow.setLocation(screenSize.width - windowSize.width-20,
screenSize.height - windowSize.height-100);
compositionWindow.setVisible(false);
}
/**
* Sets the composition area handler that currently owns this
* composition area, and its input context.
*/
synchronized void setHandlerInfo(CompositionAreaHandler handler, InputContext inputContext) {
this.handler = handler;
((InputMethodWindow) compositionWindow).setInputContext(inputContext);
}
/**
* @see java.awt.Component#getInputMethodRequests
*/
public InputMethodRequests getInputMethodRequests() {
return handler;
}
// returns a 0-width rectangle
private Rectangle getCaretRectangle(TextHitInfo caret) {
int caretLocation = 0;
TextLayout layout = composedTextLayout;
if (layout != null) {
caretLocation = Math.round(layout.getCaretInfo(caret)[0]);
}
Graphics g = getGraphics();
FontMetrics metrics = null;
try {
metrics = g.getFontMetrics();
} finally {
g.dispose();
}
return new Rectangle(TEXT_ORIGIN_X + caretLocation,
TEXT_ORIGIN_Y - metrics.getAscent(),
0, metrics.getAscent() + metrics.getDescent());
}
public void paint(Graphics g) {
super.paint(g);
g.setColor(getForeground());
TextLayout layout = composedTextLayout;
if (layout != null) {
layout.draw((Graphics2D) g, TEXT_ORIGIN_X, TEXT_ORIGIN_Y);
}
if (caret != null) {
Rectangle rectangle = getCaretRectangle(caret);
g.setXORMode(getBackground());
g.fillRect(rectangle.x, rectangle.y, 1, rectangle.height);
g.setPaintMode();
}
}
// shows/hides the composition window
void setCompositionAreaVisible(boolean visible) {
compositionWindow.setVisible(visible);
}
// returns true if composition area is visible
boolean isCompositionAreaVisible() {
return compositionWindow.isVisible();
}
// workaround for the Solaris focus lost problem
class FrameWindowAdapter extends WindowAdapter {
public void windowActivated(WindowEvent e) {
requestFocus();
}
}
// InputMethodListener methods - just forward to the current handler
public void inputMethodTextChanged(InputMethodEvent event) {
handler.inputMethodTextChanged(event);
}
public void caretPositionChanged(InputMethodEvent event) {
handler.caretPositionChanged(event);
}
/**
* Sets the text and caret to be displayed in this composition area.
* Shows the window if it contains text, hides it if not.
*/
void setText(AttributedCharacterIterator composedText, TextHitInfo caret) {
composedTextLayout = null;
if (composedText == null) {
// there's no composed text to display, so hide the window
compositionWindow.setVisible(false);
this.caret = null;
} else {
/* since we have composed text, make sure the window is shown.
This is necessary to get a valid graphics object. See 6181385.
*/
if (!compositionWindow.isVisible()) {
compositionWindow.setVisible(true);
}
Graphics g = getGraphics();
if (g == null) {
return;
}
try {
updateWindowLocation();
FontRenderContext context = ((Graphics2D)g).getFontRenderContext();
composedTextLayout = new TextLayout(composedText, context);
Rectangle2D bounds = composedTextLayout.getBounds();
this.caret = caret;
// Resize the composition area to just fit the text.
FontMetrics metrics = g.getFontMetrics();
Rectangle2D maxCharBoundsRec = metrics.getMaxCharBounds(g);
int newHeight = (int)maxCharBoundsRec.getHeight() + HEIGHT_MARGIN;
int newFrameHeight = newHeight +compositionWindow.getInsets().top
+compositionWindow.getInsets().bottom;
// If it's a passive client, set the width always to PASSIVE_WIDTH (480px)
InputMethodRequests req = handler.getClientInputMethodRequests();
int newWidth = (req==null) ? PASSIVE_WIDTH : (int)bounds.getWidth() + WIDTH_MARGIN;
int newFrameWidth = newWidth + compositionWindow.getInsets().left
+ compositionWindow.getInsets().right;
setPreferredSize(new Dimension(newWidth, newHeight));
compositionWindow.setSize(new Dimension(newFrameWidth, newFrameHeight));
// show the composed text
paint(g);
}
finally {
g.dispose();
}
}
}
/**
* Sets the caret to be displayed in this composition area.
* The text is not changed.
*/
void setCaret(TextHitInfo caret) {
this.caret = caret;
if (compositionWindow.isVisible()) {
Graphics g = getGraphics();
try {
paint(g);
} finally {
g.dispose();
}
}
}
/**
* Positions the composition window near (usually below) the
* insertion point in the client component if the client
* component is an active client (below-the-spot input).
*/
void updateWindowLocation() {
InputMethodRequests req = handler.getClientInputMethodRequests();
if (req == null) {
// not an active client
return;
}
Point windowLocation = new Point();
Rectangle caretRect = req.getTextLocation(null);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension windowSize = compositionWindow.getSize();
final int SPACING = 2;
if (caretRect.x + windowSize.width > screenSize.width) {
windowLocation.x = screenSize.width - windowSize.width;
} else {
windowLocation.x = caretRect.x;
}
if (caretRect.y + caretRect.height + SPACING + windowSize.height > screenSize.height) {
windowLocation.y = caretRect.y - SPACING - windowSize.height;
} else {
windowLocation.y = caretRect.y + caretRect.height + SPACING;
}
compositionWindow.setLocation(windowLocation);
}
// support for InputMethodRequests methods
Rectangle getTextLocation(TextHitInfo offset) {
Rectangle rectangle = getCaretRectangle(offset);
Point location = getLocationOnScreen();
rectangle.translate(location.x, location.y);
return rectangle;
}
TextHitInfo getLocationOffset(int x, int y) {
TextLayout layout = composedTextLayout;
if (layout == null) {
return null;
} else {
Point location = getLocationOnScreen();
x -= location.x + TEXT_ORIGIN_X;
y -= location.y + TEXT_ORIGIN_Y;
if (layout.getBounds().contains(x, y)) {
return layout.hitTestChar(x, y);
} else {
return null;
}
}
}
// Disables or enables decorations of the composition window
void setCompositionAreaUndecorated(boolean setUndecorated){
if (compositionWindow.isDisplayable()){
compositionWindow.removeNotify();
}
compositionWindow.setUndecorated(setUndecorated);
compositionWindow.pack();
}
}