316 lines · 11.3 KB · Java
RawDownload
/* * 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();    } }