Home » openjdk-7 » sun » awt » im » [javadoc | source]

    1   /*
    2    * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package sun.awt.im;
   27   
   28   import java.awt.AWTEvent;
   29   import java.awt.Color;
   30   import java.awt.Dimension;
   31   import java.awt.FontMetrics;
   32   import java.awt.Graphics;
   33   import java.awt.Graphics2D;
   34   import java.awt.Point;
   35   import java.awt.Rectangle;
   36   import java.awt.Toolkit;
   37   import java.awt.event.InputMethodEvent;
   38   import java.awt.event.InputMethodListener;
   39   import java.awt.event.WindowEvent;
   40   import java.awt.event.WindowAdapter;
   41   import java.awt.font.FontRenderContext;
   42   import java.awt.font.TextHitInfo;
   43   import java.awt.font.TextLayout;
   44   import java.awt.geom.Rectangle2D;
   45   import java.awt.im.InputMethodRequests;
   46   import java.text.AttributedCharacterIterator;
   47   import javax.swing.JFrame;
   48   import javax.swing.JPanel;
   49   import javax.swing.border.LineBorder;
   50   
   51   /**
   52    * A composition area is used to display text that's being composed
   53    * using an input method in its own user interface environment,
   54    * typically in a root window.
   55    *
   56    * @author JavaSoft International
   57    */
   58   
   59   // This class is final due to the 6607310 fix. Refer to the CR for details.
   60   public final class CompositionArea extends JPanel implements InputMethodListener {
   61   
   62       private CompositionAreaHandler handler;
   63   
   64       private TextLayout composedTextLayout;
   65       private TextHitInfo caret = null;
   66       private JFrame compositionWindow;
   67       private final static int TEXT_ORIGIN_X = 5;
   68       private final static int TEXT_ORIGIN_Y = 15;
   69       private final static int PASSIVE_WIDTH = 480;
   70       private final static int WIDTH_MARGIN=10;
   71       private final static int HEIGHT_MARGIN=3;
   72   
   73       CompositionArea() {
   74           // create composition window with localized title
   75           String windowTitle = Toolkit.getProperty("AWT.CompositionWindowTitle", "Input Window");
   76           compositionWindow =
   77               (JFrame)InputMethodContext.createInputMethodWindow(windowTitle, null, true);
   78   
   79           setOpaque(true);
   80           setBorder(LineBorder.createGrayLineBorder());
   81           setForeground(Color.black);
   82           setBackground(Color.white);
   83   
   84           // if we get the focus, we still want to let the client's
   85           // input context handle the event
   86           enableInputMethods(true);
   87           enableEvents(AWTEvent.KEY_EVENT_MASK);
   88   
   89           compositionWindow.getContentPane().add(this);
   90           compositionWindow.addWindowListener(new FrameWindowAdapter());
   91           addInputMethodListener(this);
   92           compositionWindow.enableInputMethods(false);
   93           compositionWindow.pack();
   94           Dimension windowSize = compositionWindow.getSize();
   95           Dimension screenSize = (getToolkit()).getScreenSize();
   96           compositionWindow.setLocation(screenSize.width - windowSize.width-20,
   97                                       screenSize.height - windowSize.height-100);
   98           compositionWindow.setVisible(false);
   99       }
  100   
  101       /**
  102        * Sets the composition area handler that currently owns this
  103        * composition area, and its input context.
  104        */
  105       synchronized void setHandlerInfo(CompositionAreaHandler handler, InputContext inputContext) {
  106           this.handler = handler;
  107           ((InputMethodWindow) compositionWindow).setInputContext(inputContext);
  108       }
  109   
  110       /**
  111        * @see java.awt.Component#getInputMethodRequests
  112        */
  113       public InputMethodRequests getInputMethodRequests() {
  114           return handler;
  115       }
  116   
  117       // returns a 0-width rectangle
  118       private Rectangle getCaretRectangle(TextHitInfo caret) {
  119           int caretLocation = 0;
  120           TextLayout layout = composedTextLayout;
  121           if (layout != null) {
  122               caretLocation = Math.round(layout.getCaretInfo(caret)[0]);
  123           }
  124           Graphics g = getGraphics();
  125           FontMetrics metrics = null;
  126           try {
  127               metrics = g.getFontMetrics();
  128           } finally {
  129               g.dispose();
  130           }
  131           return new Rectangle(TEXT_ORIGIN_X + caretLocation,
  132                                TEXT_ORIGIN_Y - metrics.getAscent(),
  133                                0, metrics.getAscent() + metrics.getDescent());
  134       }
  135   
  136       public void paint(Graphics g) {
  137           super.paint(g);
  138           g.setColor(getForeground());
  139           TextLayout layout = composedTextLayout;
  140           if (layout != null) {
  141               layout.draw((Graphics2D) g, TEXT_ORIGIN_X, TEXT_ORIGIN_Y);
  142           }
  143           if (caret != null) {
  144               Rectangle rectangle = getCaretRectangle(caret);
  145               g.setXORMode(getBackground());
  146               g.fillRect(rectangle.x, rectangle.y, 1, rectangle.height);
  147               g.setPaintMode();
  148           }
  149       }
  150   
  151       // shows/hides the composition window
  152       void setCompositionAreaVisible(boolean visible) {
  153           compositionWindow.setVisible(visible);
  154       }
  155   
  156       // returns true if composition area is visible
  157       boolean isCompositionAreaVisible() {
  158           return compositionWindow.isVisible();
  159       }
  160   
  161       // workaround for the Solaris focus lost problem
  162       class FrameWindowAdapter extends WindowAdapter {
  163           public void windowActivated(WindowEvent e) {
  164               requestFocus();
  165           }
  166       }
  167   
  168       // InputMethodListener methods - just forward to the current handler
  169       public void inputMethodTextChanged(InputMethodEvent event) {
  170           handler.inputMethodTextChanged(event);
  171       }
  172   
  173       public void caretPositionChanged(InputMethodEvent event) {
  174           handler.caretPositionChanged(event);
  175       }
  176   
  177       /**
  178        * Sets the text and caret to be displayed in this composition area.
  179        * Shows the window if it contains text, hides it if not.
  180        */
  181       void setText(AttributedCharacterIterator composedText, TextHitInfo caret) {
  182           composedTextLayout = null;
  183           if (composedText == null) {
  184               // there's no composed text to display, so hide the window
  185               compositionWindow.setVisible(false);
  186               this.caret = null;
  187           } else {
  188               /* since we have composed text, make sure the window is shown.
  189                  This is necessary to get a valid graphics object. See 6181385.
  190               */
  191               if (!compositionWindow.isVisible()) {
  192                   compositionWindow.setVisible(true);
  193               }
  194   
  195               Graphics g = getGraphics();
  196   
  197               if (g == null) {
  198                   return;
  199               }
  200   
  201               try {
  202                   updateWindowLocation();
  203   
  204                   FontRenderContext context = ((Graphics2D)g).getFontRenderContext();
  205                   composedTextLayout = new TextLayout(composedText, context);
  206                   Rectangle2D bounds = composedTextLayout.getBounds();
  207   
  208                   this.caret = caret;
  209   
  210                   // Resize the composition area to just fit the text.
  211                   FontMetrics metrics = g.getFontMetrics();
  212                   Rectangle2D maxCharBoundsRec = metrics.getMaxCharBounds(g);
  213                   int newHeight = (int)maxCharBoundsRec.getHeight() + HEIGHT_MARGIN;
  214                   int newFrameHeight = newHeight +compositionWindow.getInsets().top
  215                                                  +compositionWindow.getInsets().bottom;
  216                   // If it's a passive client, set the width always to PASSIVE_WIDTH (480px)
  217                   InputMethodRequests req = handler.getClientInputMethodRequests();
  218                   int newWidth = (req==null) ? PASSIVE_WIDTH : (int)bounds.getWidth() + WIDTH_MARGIN;
  219                   int newFrameWidth = newWidth + compositionWindow.getInsets().left
  220                                                + compositionWindow.getInsets().right;
  221                   setPreferredSize(new Dimension(newWidth, newHeight));
  222                   compositionWindow.setSize(new Dimension(newFrameWidth, newFrameHeight));
  223   
  224                   // show the composed text
  225                   paint(g);
  226               }
  227               finally {
  228                   g.dispose();
  229               }
  230           }
  231       }
  232   
  233       /**
  234        * Sets the caret to be displayed in this composition area.
  235        * The text is not changed.
  236        */
  237       void setCaret(TextHitInfo caret) {
  238           this.caret = caret;
  239           if (compositionWindow.isVisible()) {
  240               Graphics g = getGraphics();
  241               try {
  242                   paint(g);
  243               } finally {
  244                   g.dispose();
  245               }
  246           }
  247       }
  248   
  249       /**
  250        * Positions the composition window near (usually below) the
  251        * insertion point in the client component if the client
  252        * component is an active client (below-the-spot input).
  253        */
  254       void updateWindowLocation() {
  255           InputMethodRequests req = handler.getClientInputMethodRequests();
  256           if (req == null) {
  257               // not an active client
  258               return;
  259           }
  260   
  261           Point windowLocation = new Point();
  262   
  263           Rectangle caretRect = req.getTextLocation(null);
  264           Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
  265           Dimension windowSize = compositionWindow.getSize();
  266           final int SPACING = 2;
  267   
  268           if (caretRect.x + windowSize.width > screenSize.width) {
  269               windowLocation.x = screenSize.width - windowSize.width;
  270           } else {
  271               windowLocation.x = caretRect.x;
  272           }
  273   
  274           if (caretRect.y + caretRect.height + SPACING + windowSize.height > screenSize.height) {
  275               windowLocation.y = caretRect.y - SPACING - windowSize.height;
  276           } else {
  277               windowLocation.y = caretRect.y + caretRect.height + SPACING;
  278           }
  279   
  280           compositionWindow.setLocation(windowLocation);
  281       }
  282   
  283       // support for InputMethodRequests methods
  284       Rectangle getTextLocation(TextHitInfo offset) {
  285           Rectangle rectangle = getCaretRectangle(offset);
  286           Point location = getLocationOnScreen();
  287           rectangle.translate(location.x, location.y);
  288           return rectangle;
  289       }
  290   
  291      TextHitInfo getLocationOffset(int x, int y) {
  292           TextLayout layout = composedTextLayout;
  293           if (layout == null) {
  294               return null;
  295           } else {
  296               Point location = getLocationOnScreen();
  297               x -= location.x + TEXT_ORIGIN_X;
  298               y -= location.y + TEXT_ORIGIN_Y;
  299               if (layout.getBounds().contains(x, y)) {
  300                   return layout.hitTestChar(x, y);
  301               } else {
  302                   return null;
  303               }
  304           }
  305       }
  306   
  307       // Disables or enables decorations of the composition window
  308       void setCompositionAreaUndecorated(boolean setUndecorated){
  309             if (compositionWindow.isDisplayable()){
  310                 compositionWindow.removeNotify();
  311             }
  312             compositionWindow.setUndecorated(setUndecorated);
  313             compositionWindow.pack();
  314       }
  315   
  316   }

Home » openjdk-7 » sun » awt » im » [javadoc | source]