0N/A/*
2362N/A * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.awt.im;
0N/A
0N/Aimport java.awt.AWTEvent;
0N/Aimport java.awt.Color;
0N/Aimport java.awt.Dimension;
0N/Aimport java.awt.FontMetrics;
0N/Aimport java.awt.Graphics;
0N/Aimport java.awt.Graphics2D;
0N/Aimport java.awt.Point;
0N/Aimport java.awt.Rectangle;
0N/Aimport java.awt.Toolkit;
0N/Aimport java.awt.event.InputMethodEvent;
0N/Aimport java.awt.event.InputMethodListener;
0N/Aimport java.awt.event.WindowEvent;
0N/Aimport java.awt.event.WindowAdapter;
0N/Aimport java.awt.font.FontRenderContext;
0N/Aimport java.awt.font.TextHitInfo;
0N/Aimport java.awt.font.TextLayout;
0N/Aimport java.awt.geom.Rectangle2D;
0N/Aimport java.awt.im.InputMethodRequests;
0N/Aimport java.text.AttributedCharacterIterator;
0N/Aimport javax.swing.JFrame;
0N/Aimport javax.swing.JPanel;
0N/Aimport javax.swing.border.LineBorder;
0N/A
0N/A/**
0N/A * A composition area is used to display text that's being composed
0N/A * using an input method in its own user interface environment,
0N/A * typically in a root window.
0N/A *
0N/A * @author JavaSoft International
0N/A */
0N/A
635N/A// This class is final due to the 6607310 fix. Refer to the CR for details.
635N/Apublic final class CompositionArea extends JPanel implements InputMethodListener {
0N/A
0N/A private CompositionAreaHandler handler;
0N/A
0N/A private TextLayout composedTextLayout;
0N/A private TextHitInfo caret = null;
0N/A private JFrame compositionWindow;
0N/A private final static int TEXT_ORIGIN_X = 5;
0N/A private final static int TEXT_ORIGIN_Y = 15;
0N/A private final static int PASSIVE_WIDTH = 480;
0N/A private final static int WIDTH_MARGIN=10;
0N/A private final static int HEIGHT_MARGIN=3;
0N/A
0N/A CompositionArea() {
0N/A // create composition window with localized title
0N/A String windowTitle = Toolkit.getProperty("AWT.CompositionWindowTitle", "Input Window");
0N/A compositionWindow =
0N/A (JFrame)InputMethodContext.createInputMethodWindow(windowTitle, null, true);
0N/A
0N/A setOpaque(true);
0N/A setBorder(LineBorder.createGrayLineBorder());
0N/A setForeground(Color.black);
0N/A setBackground(Color.white);
0N/A
0N/A // if we get the focus, we still want to let the client's
0N/A // input context handle the event
0N/A enableInputMethods(true);
0N/A enableEvents(AWTEvent.KEY_EVENT_MASK);
0N/A
0N/A compositionWindow.getContentPane().add(this);
0N/A compositionWindow.addWindowListener(new FrameWindowAdapter());
0N/A addInputMethodListener(this);
0N/A compositionWindow.enableInputMethods(false);
0N/A compositionWindow.pack();
0N/A Dimension windowSize = compositionWindow.getSize();
0N/A Dimension screenSize = (getToolkit()).getScreenSize();
0N/A compositionWindow.setLocation(screenSize.width - windowSize.width-20,
0N/A screenSize.height - windowSize.height-100);
0N/A compositionWindow.setVisible(false);
0N/A }
0N/A
0N/A /**
0N/A * Sets the composition area handler that currently owns this
0N/A * composition area, and its input context.
0N/A */
0N/A synchronized void setHandlerInfo(CompositionAreaHandler handler, InputContext inputContext) {
0N/A this.handler = handler;
0N/A ((InputMethodWindow) compositionWindow).setInputContext(inputContext);
0N/A }
0N/A
0N/A /**
0N/A * @see java.awt.Component#getInputMethodRequests
0N/A */
0N/A public InputMethodRequests getInputMethodRequests() {
0N/A return handler;
0N/A }
0N/A
0N/A // returns a 0-width rectangle
0N/A private Rectangle getCaretRectangle(TextHitInfo caret) {
0N/A int caretLocation = 0;
0N/A TextLayout layout = composedTextLayout;
0N/A if (layout != null) {
0N/A caretLocation = Math.round(layout.getCaretInfo(caret)[0]);
0N/A }
0N/A Graphics g = getGraphics();
0N/A FontMetrics metrics = null;
0N/A try {
0N/A metrics = g.getFontMetrics();
0N/A } finally {
0N/A g.dispose();
0N/A }
0N/A return new Rectangle(TEXT_ORIGIN_X + caretLocation,
0N/A TEXT_ORIGIN_Y - metrics.getAscent(),
0N/A 0, metrics.getAscent() + metrics.getDescent());
0N/A }
0N/A
0N/A public void paint(Graphics g) {
0N/A super.paint(g);
0N/A g.setColor(getForeground());
0N/A TextLayout layout = composedTextLayout;
0N/A if (layout != null) {
0N/A layout.draw((Graphics2D) g, TEXT_ORIGIN_X, TEXT_ORIGIN_Y);
0N/A }
0N/A if (caret != null) {
0N/A Rectangle rectangle = getCaretRectangle(caret);
0N/A g.setXORMode(getBackground());
0N/A g.fillRect(rectangle.x, rectangle.y, 1, rectangle.height);
0N/A g.setPaintMode();
0N/A }
0N/A }
0N/A
0N/A // shows/hides the composition window
0N/A void setCompositionAreaVisible(boolean visible) {
0N/A compositionWindow.setVisible(visible);
0N/A }
0N/A
0N/A // returns true if composition area is visible
0N/A boolean isCompositionAreaVisible() {
0N/A return compositionWindow.isVisible();
0N/A }
0N/A
0N/A // workaround for the Solaris focus lost problem
0N/A class FrameWindowAdapter extends WindowAdapter {
0N/A public void windowActivated(WindowEvent e) {
0N/A requestFocus();
0N/A }
0N/A }
0N/A
0N/A // InputMethodListener methods - just forward to the current handler
0N/A public void inputMethodTextChanged(InputMethodEvent event) {
0N/A handler.inputMethodTextChanged(event);
0N/A }
0N/A
0N/A public void caretPositionChanged(InputMethodEvent event) {
0N/A handler.caretPositionChanged(event);
0N/A }
0N/A
0N/A /**
0N/A * Sets the text and caret to be displayed in this composition area.
0N/A * Shows the window if it contains text, hides it if not.
0N/A */
0N/A void setText(AttributedCharacterIterator composedText, TextHitInfo caret) {
0N/A composedTextLayout = null;
0N/A if (composedText == null) {
0N/A // there's no composed text to display, so hide the window
0N/A compositionWindow.setVisible(false);
0N/A this.caret = null;
0N/A } else {
0N/A /* since we have composed text, make sure the window is shown.
0N/A This is necessary to get a valid graphics object. See 6181385.
0N/A */
0N/A if (!compositionWindow.isVisible()) {
0N/A compositionWindow.setVisible(true);
0N/A }
0N/A
0N/A Graphics g = getGraphics();
0N/A
0N/A if (g == null) {
0N/A return;
0N/A }
0N/A
0N/A try {
0N/A updateWindowLocation();
0N/A
0N/A FontRenderContext context = ((Graphics2D)g).getFontRenderContext();
0N/A composedTextLayout = new TextLayout(composedText, context);
0N/A Rectangle2D bounds = composedTextLayout.getBounds();
0N/A
0N/A this.caret = caret;
0N/A
0N/A // Resize the composition area to just fit the text.
0N/A FontMetrics metrics = g.getFontMetrics();
0N/A Rectangle2D maxCharBoundsRec = metrics.getMaxCharBounds(g);
0N/A int newHeight = (int)maxCharBoundsRec.getHeight() + HEIGHT_MARGIN;
0N/A int newFrameHeight = newHeight +compositionWindow.getInsets().top
0N/A +compositionWindow.getInsets().bottom;
0N/A // If it's a passive client, set the width always to PASSIVE_WIDTH (480px)
0N/A InputMethodRequests req = handler.getClientInputMethodRequests();
0N/A int newWidth = (req==null) ? PASSIVE_WIDTH : (int)bounds.getWidth() + WIDTH_MARGIN;
0N/A int newFrameWidth = newWidth + compositionWindow.getInsets().left
0N/A + compositionWindow.getInsets().right;
0N/A setPreferredSize(new Dimension(newWidth, newHeight));
0N/A compositionWindow.setSize(new Dimension(newFrameWidth, newFrameHeight));
0N/A
0N/A // show the composed text
0N/A paint(g);
0N/A }
0N/A finally {
0N/A g.dispose();
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Sets the caret to be displayed in this composition area.
0N/A * The text is not changed.
0N/A */
0N/A void setCaret(TextHitInfo caret) {
0N/A this.caret = caret;
0N/A if (compositionWindow.isVisible()) {
0N/A Graphics g = getGraphics();
0N/A try {
0N/A paint(g);
0N/A } finally {
0N/A g.dispose();
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Positions the composition window near (usually below) the
0N/A * insertion point in the client component if the client
0N/A * component is an active client (below-the-spot input).
0N/A */
0N/A void updateWindowLocation() {
0N/A InputMethodRequests req = handler.getClientInputMethodRequests();
0N/A if (req == null) {
0N/A // not an active client
0N/A return;
0N/A }
0N/A
0N/A Point windowLocation = new Point();
0N/A
0N/A Rectangle caretRect = req.getTextLocation(null);
0N/A Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
0N/A Dimension windowSize = compositionWindow.getSize();
0N/A final int SPACING = 2;
0N/A
0N/A if (caretRect.x + windowSize.width > screenSize.width) {
0N/A windowLocation.x = screenSize.width - windowSize.width;
0N/A } else {
0N/A windowLocation.x = caretRect.x;
0N/A }
0N/A
0N/A if (caretRect.y + caretRect.height + SPACING + windowSize.height > screenSize.height) {
0N/A windowLocation.y = caretRect.y - SPACING - windowSize.height;
0N/A } else {
0N/A windowLocation.y = caretRect.y + caretRect.height + SPACING;
0N/A }
0N/A
0N/A compositionWindow.setLocation(windowLocation);
0N/A }
0N/A
0N/A // support for InputMethodRequests methods
0N/A Rectangle getTextLocation(TextHitInfo offset) {
0N/A Rectangle rectangle = getCaretRectangle(offset);
0N/A Point location = getLocationOnScreen();
0N/A rectangle.translate(location.x, location.y);
0N/A return rectangle;
0N/A }
0N/A
0N/A TextHitInfo getLocationOffset(int x, int y) {
0N/A TextLayout layout = composedTextLayout;
0N/A if (layout == null) {
0N/A return null;
0N/A } else {
0N/A Point location = getLocationOnScreen();
0N/A x -= location.x + TEXT_ORIGIN_X;
0N/A y -= location.y + TEXT_ORIGIN_Y;
0N/A if (layout.getBounds().contains(x, y)) {
0N/A return layout.hitTestChar(x, y);
0N/A } else {
0N/A return null;
0N/A }
0N/A }
0N/A }
0N/A
0N/A // Disables or enables decorations of the composition window
0N/A void setCompositionAreaUndecorated(boolean setUndecorated){
0N/A if (compositionWindow.isDisplayable()){
0N/A compositionWindow.removeNotify();
0N/A }
0N/A compositionWindow.setUndecorated(setUndecorated);
0N/A compositionWindow.pack();
0N/A }
0N/A
0N/A}