/* * This file is modified by Ivan Maidanski <ivmai@ivmaisoft.com> * Project name: JCGO-SUNAWT (http://www.ivmaisoft.com/jcgo/) */ /* * @(#)CompositionArea.java 1.30 03/01/23 * * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ 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.im.InputMethodRequests; import java.text.AttributedCharacterIterator; import javax.swing.JFrame; import javax.swing.JPanel; /** * 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. * * @version 1.30 01/23/03 * @author JavaSoft International */ public class CompositionArea extends java.awt.Canvas/*JPanel*/ implements InputMethodListener { private CompositionAreaHandler handler; private TextLayout composedTextLayout; private TextHitInfo caret = null; private java.awt.Window/*JFrame*/ compositionWindow; private final static int TEXT_ORIGIN_X = 5; private final static int TEXT_ORIGIN_Y = 15; CompositionArea() { // create composition window with localized title String windowTitle = Toolkit.getProperty("AWT.CompositionWindowTitle", "Input Window"); /*compositionWindow = (JFrame)InputMethodContext.createInputMethodWindow(windowTitle, null, true); setPreferredSize(new Dimension(480, 18)); setOpaque(true);*/ if (java.awt.GraphicsEnvironment.isHeadless()) throw new java.awt.HeadlessException(); Toolkit toolkit = Toolkit.getDefaultToolkit(); if (!(toolkit instanceof sun.awt.InputMethodSupport)) throw new InternalError("Input methods must be supported"); compositionWindow = ((sun.awt.InputMethodSupport)toolkit).createInputMethodWindow( windowTitle, null); setSize(480, 18); 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(); } } // 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 { // show the composed text Graphics g = getGraphics(); FontRenderContext context = null; try { context = ((Graphics2D)g).getFontRenderContext(); } finally { g.dispose(); } composedTextLayout = new TextLayout(composedText, context); this.caret = caret; // since we have composed text, make sure the window is shown updateWindowLocation(); if (!compositionWindow.isVisible()) { compositionWindow.setVisible(true); } g = getGraphics(); try { 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; } } } }