home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / JBuilder8.iso / Solaris / resource / jre / demo / jfc / Font2DTest / src / FontPanel.java < prev    next >
Encoding:
Java Source  |  2002-09-06  |  48.5 KB  |  1,243 lines

  1. /*
  2.  * Copyright (c) 2002 Sun Microsystems, Inc. All  Rights Reserved.
  3.  * 
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  * 
  8.  * -Redistributions of source code must retain the above copyright
  9.  *  notice, this list of conditions and the following disclaimer.
  10.  * 
  11.  * -Redistribution in binary form must reproduct the above copyright
  12.  *  notice, this list of conditions and the following disclaimer in
  13.  *  the documentation and/or other materials provided with the distribution.
  14.  * 
  15.  * Neither the name of Sun Microsystems, Inc. or the names of contributors
  16.  * may be used to endorse or promote products derived from this software
  17.  * without specific prior written permission.
  18.  * 
  19.  * This software is provided "AS IS," without a warranty of any kind. ALL
  20.  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
  21.  * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
  22.  * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT
  23.  * BE LIABLE FOR ANY DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT
  24.  * OF OR RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR ITS
  25.  * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
  26.  * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
  27.  * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
  28.  * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN
  29.  * IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
  30.  * 
  31.  * You acknowledge that Software is not designed, licensed or intended for
  32.  * use in the design, construction, operation or maintenance of any nuclear
  33.  * facility.
  34.  */
  35.  
  36. /*
  37.  * @(#)FontPanel.java    1.13 02/06/13
  38.  */
  39.  
  40. import java.awt.BorderLayout;
  41. import java.awt.Color;
  42. import java.awt.Cursor;
  43. import java.awt.Dimension;
  44. import java.awt.Font;
  45. import java.awt.FontMetrics;
  46. import java.awt.Graphics;
  47. import java.awt.Graphics2D;
  48. import java.awt.Point;
  49. import java.awt.Rectangle;
  50. import java.awt.RenderingHints;
  51. import java.awt.Toolkit;
  52. import java.awt.event.AdjustmentEvent;
  53. import java.awt.event.AdjustmentListener;
  54. import java.awt.event.ComponentAdapter;
  55. import java.awt.event.ComponentEvent;
  56. import java.awt.event.MouseEvent;
  57. import java.awt.event.MouseListener;
  58. import java.awt.event.MouseMotionListener;
  59. import java.awt.font.FontRenderContext;
  60. import java.awt.font.GlyphVector;
  61. import java.awt.font.LineBreakMeasurer;
  62. import java.awt.font.TextLayout;
  63. import java.awt.geom.AffineTransform;
  64. import java.awt.geom.NoninvertibleTransformException;
  65. import java.awt.geom.Rectangle2D;
  66. import java.awt.image.BufferedImage;
  67. import java.awt.print.PageFormat;
  68. import java.awt.print.Printable;
  69. import java.awt.print.PrinterJob;
  70. import java.io.BufferedOutputStream;
  71. import java.io.FileOutputStream;
  72. import java.text.AttributedString;
  73. import java.util.Vector;
  74.  
  75. import javax.swing.*;
  76.  
  77. import com.sun.image.codec.jpeg.JPEGCodec;
  78. import com.sun.image.codec.jpeg.JPEGImageEncoder;
  79. import com.sun.image.codec.jpeg.JPEGEncodeParam;
  80.  
  81. /**
  82.  * FontPanel.java
  83.  *
  84.  * @version @(#)FontPanel.java    1.1 00/08/22
  85.  * @author Shinsuke Fukuda
  86.  * @author Ankit Patel [Conversion to Swing - 01/07/30] 
  87.  */
  88.  
  89. /// This panel is combination of the text drawing area of Font2DTest
  90. /// and the custom controlled scroll bar
  91.  
  92. public final class FontPanel extends JPanel implements AdjustmentListener {
  93.  
  94.     /// Drawing Option Constants
  95.     private final String STYLES[] =
  96.       { "plain", "bold", "italic", "bold italic" };
  97.  
  98.     private final int NONE = 0;
  99.     private final int SCALE = 1;
  100.     private final int SHEAR = 2;
  101.     private final int ROTATE = 3;
  102.     private final String TRANSFORMS[] =
  103.       { "with no transforms", "with scaling", "with Shearing", "with rotation" };
  104.  
  105.     private final int DRAW_STRING = 0;
  106.     private final int DRAW_CHARS = 1;
  107.     private final int DRAW_BYTES = 2;
  108.     private final int DRAW_GLYPHV = 3;
  109.     private final int TL_DRAW = 4;
  110.     private final int GV_OUTLINE = 5;
  111.     private final int TL_OUTLINE = 6;
  112.     private final String METHODS[] = {
  113.         "drawString", "drawChars", "drawBytes", "drawGlyphVector",
  114.         "TextLayout.draw", "GlyphVector.getOutline", "TextLayout.getOutline" };
  115.  
  116.     public final int RANGE_TEXT = 0;
  117.     public final int ALL_GLYPHS = 1;
  118.     public final int USER_TEXT = 2;
  119.     public final int FILE_TEXT = 3;
  120.     private final String MS_OPENING[] =
  121.       { " Unicode ", " Glyph Code ", " lines ", " lines " };
  122.     private final String MS_CLOSING[] =
  123.       { "", "", " of User Text ", " of LineBreakMeasurer-reformatted Text " };
  124.  
  125.     /// General Graphics Variable
  126.     private final JScrollBar verticalBar;
  127.     private final FontCanvas fc;
  128.     private boolean updateBackBuffer = true;
  129.     private boolean updateFontMetrics = true;
  130.     private boolean updateFont = true;
  131.     private boolean force16Cols = false;
  132.     public boolean showingError = false;
  133.     private int g2Transform = NONE; /// ABP
  134.  
  135.     /// Printing constants and variables
  136.     public final int ONE_PAGE = 0;
  137.     public final int CUR_RANGE = 1;
  138.     public final int ALL_TEXT = 2;
  139.     private int printMode = ONE_PAGE;
  140.     private PageFormat page = null;
  141.     private PrinterJob printer = null;
  142.  
  143.     /// Text drawing variables
  144.     private String fontName = "Dialog";
  145.     private int fontSize = 12;
  146.     private int fontStyle = Font.PLAIN;
  147.     private int fontTransform = NONE;
  148.     private Font testFont = null;
  149.     private boolean useAntialias = false;
  150.     private boolean useFractional = false;
  151.     private int drawMethod = DRAW_STRING;
  152.     private int textToUse = RANGE_TEXT;
  153.     private String userText[] = null;
  154.     private String fileText[] = null;
  155.     private int drawRange[] = { 0x0000, 0x007f };
  156.     private String fontInfos[] = new String[2];
  157.     private boolean showGrid = true;
  158.  
  159.     /// Parent Font2DTest panel
  160.     private final Font2DTest f2dt;
  161.     private final JFrame parent;
  162.  
  163.     public FontPanel( Font2DTest demo, JFrame f ) {
  164.         f2dt = demo;
  165.         parent = f;
  166.  
  167.         verticalBar = new JScrollBar ( JScrollBar.VERTICAL );
  168.         fc = new FontCanvas();
  169.  
  170.         this.setLayout( new BorderLayout() );
  171.         this.add( "Center", fc );
  172.         this.add( "East", verticalBar );
  173.  
  174.         verticalBar.addAdjustmentListener( this );
  175.         this.addComponentListener( new ComponentAdapter() {
  176.             public void componentResized( ComponentEvent e ) {
  177.                 updateBackBuffer = true;
  178.                 updateFontMetrics = true;
  179.             }
  180.         });
  181.  
  182.         /// Initialize font and its infos
  183.         testFont = new Font( fontName, fontStyle, fontSize );
  184.         updateFontInfo();
  185.     }
  186.  
  187.     public Dimension getPreferredSize() {
  188.         return new Dimension(600, 200);
  189.     }
  190.  
  191.     /// Functions called by the main programs to set the various parameters
  192.  
  193.     public void setTransformG2( int transform ) {
  194.         g2Transform = transform;
  195.         updateBackBuffer = true;
  196.         updateFontMetrics = true;
  197.         fc.repaint();
  198.     }
  199.     
  200.     /// convenience fcn to create AffineTransform of appropriate type
  201.     private AffineTransform getAffineTransform( int transform ) {
  202.         /// ABP
  203.             AffineTransform at = new AffineTransform();
  204.             switch ( transform )
  205.             {
  206.             case SCALE:
  207.               at.setToScale( 1.5f, 1.5f ); break;
  208.             case ROTATE:
  209.               at.setToRotation( Math.PI / 6 ); break;
  210.             case SHEAR:
  211.               at.setToShear( 0.4f, 0 ); break;
  212.             case NONE:
  213.               break;
  214.             default:
  215.               //System.err.println( "Illegal G2 Transform Arg: " + transform); 
  216.               break;
  217.             }
  218.  
  219.              return at;            
  220.     }
  221.  
  222.     public void setFontParams( Object obj, int size, int style, int transform ) {
  223.         setFontParams( (String)obj, size, style, transform );
  224.     }
  225.  
  226.     public void setFontParams( String name, int size, int style, int transform ) {
  227.         boolean fontModified = false;
  228.         if ( !name.equals( fontName ) || style != fontStyle )
  229.           fontModified = true;
  230.  
  231.         fontName = name;
  232.         fontSize = size;
  233.         fontStyle = style;
  234.         fontTransform = transform;
  235.  
  236.         /// Recreate the font as specified
  237.         testFont = new Font( fontName, fontStyle, fontSize );
  238.  
  239.         if ( fontTransform != NONE ) {
  240.             AffineTransform at = getAffineTransform( fontTransform );
  241.             testFont = testFont.deriveFont( at );
  242.         }
  243.         updateBackBuffer = true;
  244.         updateFontMetrics = true;
  245.         fc.repaint();
  246.         if ( fontModified ) {
  247.             /// Tell main panel to update the font info
  248.             updateFontInfo();
  249.             f2dt.fireUpdateFontInfo();
  250.         }
  251.     }
  252.  
  253.     public void setRenderingHints( boolean aa, boolean fm ) {
  254.         useAntialias = aa;
  255.         useFractional = fm;
  256.         updateBackBuffer = true;
  257.         updateFontMetrics = true;
  258.         fc.repaint();
  259.     }
  260.  
  261.     public void setDrawMethod( int i ) {
  262.         drawMethod = i;
  263.         updateBackBuffer = true;
  264.         fc.repaint();
  265.     }
  266.  
  267.     public void setTextToDraw( int i, int range[],
  268.                                String textSet[], String fileData[] ) {
  269.         textToUse = i;
  270.  
  271.         if ( textToUse == RANGE_TEXT )
  272.           drawRange = range;
  273.         else if ( textToUse == ALL_GLYPHS )
  274.           drawMethod = DRAW_GLYPHV;
  275.         else if ( textToUse == USER_TEXT )
  276.           userText = textSet;
  277.         else if ( textToUse == FILE_TEXT ) {
  278.             fileText = fileData;
  279.             drawMethod = TL_DRAW;
  280.         }
  281.  
  282.         updateBackBuffer = true;
  283.         updateFontMetrics = true;
  284.         fc.repaint();
  285.         updateFontInfo();
  286.     }
  287.  
  288.     public void setGridDisplay( boolean b ) {
  289.         showGrid = b;
  290.         updateBackBuffer = true;
  291.         fc.repaint();
  292.     }
  293.  
  294.     public void setForce16Columns( boolean b ) {
  295.         force16Cols = b;
  296.         updateBackBuffer = true;
  297.         updateFontMetrics = true;
  298.         fc.repaint();
  299.     }
  300.  
  301.     /// Prints out the text display area
  302.     public void doPrint( int i ) {
  303.         if ( printer == null ) {
  304.             printer = PrinterJob.getPrinterJob();
  305.             page = printer.defaultPage();
  306.         }
  307.         printMode = i;
  308.         printer.setPrintable( fc, page );
  309.  
  310.         if ( printer.printDialog() ) {
  311.             try {
  312.                 printer.print();
  313.             }
  314.             catch ( Exception e ) {
  315.                 f2dt.fireChangeStatus( "ERROR: Printing Failed; See Stack Trace", true );
  316.             }
  317.         }
  318.     }
  319.  
  320.     /// Displays the page setup dialog and updates PageFormat info
  321.     public void doPageSetup() {
  322.         if ( printer == null ) {
  323.             printer = PrinterJob.getPrinterJob();
  324.             page = printer.defaultPage();
  325.         }
  326.         page = printer.pageDialog( page );
  327.     }
  328.  
  329.     /// Obtains the information about selected font
  330.     private void updateFontInfo() {
  331.         int numGlyphs = 0, numCharsInRange = drawRange[1] - drawRange[0] + 1;
  332.         fontInfos[0] = "Font Face Name: " + testFont.getFontName();
  333.         fontInfos[1] = "Glyphs in This Range: ";
  334.  
  335.         if ( textToUse == RANGE_TEXT ) {
  336.             for ( int i = drawRange[0]; i < drawRange[1]; i++ )
  337.               if ( testFont.canDisplay( (char) i ))
  338.                 numGlyphs++;
  339.             fontInfos[1] = fontInfos[1] + numGlyphs + " / " + numCharsInRange;
  340.         }
  341.         else
  342.           fontInfos[1] = null;
  343.     }
  344.  
  345.     /// Accessor for the font information
  346.     public String[] getFontInfo() {
  347.         return fontInfos;
  348.     }
  349.  
  350.     /// Collects the currectly set options and returns them as string
  351.     public String getCurrentOptions() {
  352.         /// Create a new String to store the options
  353.         /// The array will contain all 8 setting (font name, size...) and
  354.         /// character range or user text data used (no file text data)
  355.         int userTextSize = 0;
  356.         String options;
  357.  
  358.         options = ( fontName + "\n" + fontSize  + "\n" + fontStyle + "\n" +
  359.                     fontTransform + "\n"  + g2Transform + "\n"+ textToUse + "\n" + drawMethod + "\n" +
  360.                     useAntialias + "\n" + useFractional + "\n");
  361.         if ( textToUse == USER_TEXT )
  362.           for ( int i = 0; i < userText.length; i++ )
  363.             options += ( userText[i] + "\n" );
  364.  
  365.         return options;
  366.     }
  367.  
  368.     /// Reload all options and refreshes the canvas
  369.     public void loadOptions( boolean grid, boolean force16, int start, int end,
  370.                              String name, int size, int style, int transform, int g2transform,
  371.                              int text, int method, boolean aa, boolean fm,
  372.                              String user[] ) {
  373.         int range[] = { start, end };
  374.  
  375.         /// Since repaint call has a low priority, these functions will finish
  376.         /// before the actual repainting is done
  377.         setGridDisplay( grid );
  378.         setForce16Columns( force16 );
  379.     // previous call to readTextFile has already set the text to draw
  380.     if (textToUse != FILE_TEXT) {
  381.       setTextToDraw( text, range, user, null );
  382.     }
  383.         setFontParams( name, size, style, transform );
  384.         setTransformG2( g2transform ); // ABP
  385.         setDrawMethod( method );
  386.         setRenderingHints( aa, fm );
  387.     }
  388.  
  389.     /// Writes the current screen to JPEG file
  390.     public void doSaveJPEG( String fileName ) {
  391.         fc.writeJPEG( fileName );
  392.     }
  393.  
  394.     /// When scrolled using the scroll bar, update the backbuffer
  395.     public void adjustmentValueChanged( AdjustmentEvent e ) {
  396.         updateBackBuffer = true;
  397.         fc.repaint();
  398.     }
  399.  
  400.     public void paintComponent( Graphics g ) {
  401.         // Windows does not repaint correctly, after
  402.         // a zoom. Thus, we need to force the canvas
  403.         // to repaint, but only once. After the first repaint,
  404.         // everything stabilizes. [ABP]
  405.         fc.repaint();
  406.     }
  407.  
  408.     /// Inner class definition...
  409.  
  410.     /// Inner panel that holds the actual drawing area and its routines
  411.     private class FontCanvas extends JPanel implements MouseListener, MouseMotionListener, Printable {
  412.  
  413.         /// Number of characters that will fit across and down this canvas
  414.         private int numCharAcross, numCharDown;
  415.  
  416.         /// First and last character/line that will be drawn
  417.         /// Limit is the end of range/text where no more draw will be done
  418.         private int drawStart, drawEnd, drawLimit;
  419.  
  420.         /// FontMetrics variables
  421.         /// Here, gridWidth is equivalent to maxAdvance (slightly bigger though)
  422.         /// and gridHeight is equivalent to lineHeight
  423.         private int maxAscent, maxDescent, gridWidth = 0, gridHeight = 0;
  424.  
  425.         /// Offset from the top left edge of the canvas where the draw will start
  426.         private int canvasInset_X = 5, canvasInset_Y = 5;
  427.  
  428.         /// Offscreen buffer of this canvas
  429.         private BufferedImage backBuffer = null;
  430.  
  431.         /// LineBreak'ed TextLayout vector
  432.         private Vector lineBreakTLs = null;
  433.  
  434.         /// Whether the current draw command requested is for printing
  435.         private boolean isPrinting = false;
  436.  
  437.         /// Other printing infos
  438.         private int lastPage, printPageNumber, currentlyShownChar = 0;
  439.         private final int PR_OFFSET = 10;
  440.         private final int PR_TITLE_LINEHEIGHT = 30;
  441.  
  442.         /// Information about zooming (used with range text draw)
  443.         private final JWindow zoomWindow;
  444.         private BufferedImage zoomImage = null;
  445.         private int mouseOverCharX = -1, mouseOverCharY = -1;
  446.         private int currMouseOverChar = -1, prevZoomChar = -1;
  447.         private float ZOOM = 2.0f;
  448.         private boolean nowZooming = false;
  449.         private boolean firstTime = true;
  450. // ABP
  451.  
  452.         /// Status bar message backup
  453.         private String backupStatusString = null;
  454.  
  455.         /// Error constants
  456.         private final String ERRORS[] = {
  457.             "ERROR: drawBytes cannot handle characters beyond 0x00FF. Select different range or draw methods.",
  458.             "ERROR: Cannot fit text with the current font size. Resize the window or use smaller font size.",
  459.             "ERROR: Cannot print with the current font size. Use smaller font size.",
  460.         };
  461.  
  462.         private final int DRAW_BYTES_ERROR = 0;
  463.         private final int CANT_FIT_DRAW = 1;
  464.         private final int CANT_FIT_PRINT = 2;
  465.  
  466.         /// Other variables
  467.         private final Cursor blankCursor;
  468.  
  469.         public FontCanvas() {
  470.             this.addMouseListener( this );
  471.             this.addMouseMotionListener( this );
  472.             this.setForeground( Color.black );
  473.             this.setBackground( Color.white );
  474.  
  475.             /// Creates an invisble pointer by giving it bogus image
  476.             /// Possibly find a workaround for this...
  477.             Toolkit tk = Toolkit.getDefaultToolkit();
  478.             byte bogus[] = { (byte) 0 };
  479.             blankCursor =
  480.               tk.createCustomCursor( tk.createImage( bogus ), new Point(0, 0), "" );
  481.  
  482.             zoomWindow = new JWindow( parent ) {
  483.                 public void paint( Graphics g ) {
  484.                     g.drawImage( zoomImage, 0, 0, zoomWindow );
  485.                 }
  486.             };            
  487.             zoomWindow.setCursor( blankCursor );
  488.             zoomWindow.pack();
  489.         }
  490.  
  491.         public boolean firstTime() { return firstTime; }
  492.         public void refresh() {
  493.             firstTime = false;
  494.             updateBackBuffer = true;
  495.             repaint();
  496.         }
  497.  
  498.         /// Sets the font, hints, and transforms according to the set parameters
  499.         private void setParams( Graphics2D g2 ) {
  500.             g2.setFont( testFont );
  501.  
  502.             if ( useAntialias ) {
  503.                 if ( drawMethod == TL_OUTLINE || drawMethod == GV_OUTLINE )
  504.                   g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING,
  505.                                        RenderingHints.VALUE_ANTIALIAS_ON );
  506.                 else
  507.                   g2.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING,
  508.                                        RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
  509.             }
  510.             else {
  511.                 g2.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING,
  512.                                      RenderingHints.VALUE_TEXT_ANTIALIAS_OFF );
  513.                 g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING,
  514.                                      RenderingHints.VALUE_ANTIALIAS_OFF );
  515.             }
  516.  
  517.             if ( useFractional )
  518.               g2.setRenderingHint( RenderingHints.KEY_FRACTIONALMETRICS,
  519.                                    RenderingHints.VALUE_FRACTIONALMETRICS_ON );
  520.             else
  521.               g2.setRenderingHint( RenderingHints.KEY_FRACTIONALMETRICS,
  522.                                    RenderingHints.VALUE_FRACTIONALMETRICS_OFF );
  523.         }
  524.  
  525.         /// Draws the grid (Used for unicode/glyph range drawing)
  526.         private void drawGrid( Graphics2D g2 ) {
  527.             int totalGridWidth = numCharAcross * gridWidth;
  528.             int totalGridHeight = numCharDown * gridHeight;
  529.  
  530.             g2.setColor( Color.black );
  531.             for ( int i = 0; i < numCharDown + 1; i++ )
  532.               g2.drawLine( canvasInset_X, i * gridHeight + canvasInset_Y,
  533.                            canvasInset_X + totalGridWidth, i * gridHeight + canvasInset_Y );
  534.             for ( int i = 0; i < numCharAcross + 1; i++ )
  535.               g2.drawLine( i * gridWidth + canvasInset_X, canvasInset_Y,
  536.                            i * gridWidth + canvasInset_X, canvasInset_Y + totalGridHeight );
  537.         }
  538.  
  539.         /// Draws one character at time onto the canvas according to
  540.         /// the method requested (Used for RANGE_TEXT and ALL_GLYPHS)
  541.         public void modeSpecificDrawChar( Graphics2D g2, int charCode,
  542.                                           int baseX, int baseY ) {
  543.             GlyphVector gv;
  544.             int oneGlyph[] = { charCode };
  545.             char oneChar[] = { (char) charCode };
  546.  
  547.             /// Create GlyphVector to measure the exact visual advance
  548.             /// Using that number, adjust the position of the character drawn
  549.             if ( textToUse == ALL_GLYPHS )
  550.               gv = testFont.createGlyphVector( g2.getFontRenderContext(), oneGlyph );
  551.             else
  552.               gv = testFont.createGlyphVector( g2.getFontRenderContext(), oneChar );
  553.             Rectangle2D r2d2 = gv.getPixelBounds(g2.getFontRenderContext(), 0, 0);
  554.             int shiftedX = baseX;
  555.         // getPixelBounds returns a result in device space.
  556.         // we need to convert back to user space to be able to
  557.         // calculate the shift as baseX is in user space.
  558.             try {
  559.                  double pt[] = new double[4];
  560.                  pt[0] = r2d2.getX();
  561.                  pt[1] = r2d2.getY();
  562.                  pt[2] = r2d2.getX()+r2d2.getWidth();
  563.                  pt[3] = r2d2.getY()+r2d2.getHeight();
  564.                  g2.getTransform().inverseTransform(pt,0,pt,0,2);
  565.                  shiftedX = baseX - (int) ( pt[2] / 2 + pt[0] );
  566.             } catch (NoninvertibleTransformException e) {
  567.             }
  568.  
  569.         /// ABP - keep track of old tform, restore it later
  570.         AffineTransform oldTx = null;
  571.         oldTx = g2.getTransform();
  572.           g2.translate( shiftedX, baseY );              
  573.         g2.transform( getAffineTransform( g2Transform ) );
  574.  
  575.             if ( textToUse == ALL_GLYPHS )
  576.               g2.drawGlyphVector( gv, 0f, 0f );              
  577.             else {
  578.                 if ( testFont.canDisplay( (char) charCode ))
  579.                   g2.setColor( Color.black );
  580.                 else {
  581.                   g2.setColor( Color.lightGray );
  582.                 }
  583.  
  584.                 switch ( drawMethod ) {
  585.                   case DRAW_STRING:
  586.                     g2.drawString( new String( oneChar ), 0, 0 );
  587.                     break;
  588.                   case DRAW_CHARS:
  589.                     g2.drawChars( oneChar, 0, 1, 0, 0 );
  590.                     break;
  591.                   case DRAW_BYTES:
  592.                     if ( charCode > 0xff )
  593.                       throw new CannotDrawException( DRAW_BYTES_ERROR );
  594.                     byte oneByte[] = { (byte) charCode };
  595.                     g2.drawBytes( oneByte, 0, 1, 0, 0 );
  596.                     break;
  597.                   case DRAW_GLYPHV:
  598.                     g2.drawGlyphVector( gv, 0f, 0f );
  599.                     break;
  600.                   case TL_DRAW:
  601.                     TextLayout tl = new TextLayout( new String( oneChar ), testFont,
  602.                                                     g2.getFontRenderContext() );
  603.                     tl.draw( g2, 0f, 0f );
  604.                     break;
  605.                   case GV_OUTLINE:
  606.             r2d2 = gv.getVisualBounds();
  607.             shiftedX = baseX - (int) ( r2d2.getWidth() / 2 + r2d2.getX() );
  608.                     g2.draw( gv.getOutline( 0f, 0f ));
  609.                     break;
  610.                   case TL_OUTLINE:
  611.             r2d2 = gv.getVisualBounds();
  612.             shiftedX = baseX - (int) ( r2d2.getWidth() / 2 + r2d2.getX() );
  613.                     TextLayout tlo =
  614.                       new TextLayout( new String( oneChar ), testFont,
  615.                                       g2.getFontRenderContext() );
  616.                     AffineTransform at = new AffineTransform();
  617.                     g2.draw( tlo.getOutline( at ));
  618.                 }
  619.             }
  620.             
  621.         /// ABP - restore old tform
  622.             g2.setTransform ( oldTx );
  623.         }
  624.  
  625.         /// Draws one line of text at given position
  626.         private void modeSpecificDrawLine( Graphics2D g2, String line,
  627.                                            int baseX, int baseY ) {
  628.         /// ABP - keep track of old tform, restore it later
  629.         AffineTransform oldTx = null;
  630.         oldTx = g2.getTransform();
  631.           g2.translate( baseX, baseY );              
  632.         g2.transform( getAffineTransform( g2Transform ) );
  633.         
  634.             switch ( drawMethod ) {
  635.               case DRAW_STRING:
  636.                 g2.drawString( line, 0, 0 );
  637.                 break;
  638.               case DRAW_CHARS:
  639.                 g2.drawChars( line.toCharArray(), 0, line.length(), 0, 0 );
  640.                 break;
  641.               case DRAW_BYTES:
  642.                 try {
  643.                     byte lineBytes[] = line.getBytes( "ISO-8859-1" );
  644.                     g2.drawBytes( lineBytes, 0, line.length(), 0, 0 );
  645.                 }
  646.                 catch ( Exception e ) {
  647.                     e.printStackTrace();
  648.                 }
  649.                 break;
  650.               case DRAW_GLYPHV:
  651.                 GlyphVector gv =
  652.                   testFont.createGlyphVector( g2.getFontRenderContext(), line );
  653.                 g2.drawGlyphVector( gv, (float) 0, (float) 0 );
  654.                 break;
  655.               case TL_DRAW:
  656.                 TextLayout tl = new TextLayout( line, testFont,
  657.                                                 g2.getFontRenderContext() );
  658.                 tl.draw( g2, (float) 0, (float) 0 );
  659.                 break;
  660.               case GV_OUTLINE:
  661.                 GlyphVector gvo =
  662.                   testFont.createGlyphVector( g2.getFontRenderContext(), line );
  663.                 g2.draw( gvo.getOutline( (float) 0, (float) 0 ));
  664.                 break;
  665.               case TL_OUTLINE:
  666.                 TextLayout tlo =
  667.                   new TextLayout( line, testFont,
  668.                                   g2.getFontRenderContext() );
  669.                 AffineTransform at = new AffineTransform();
  670.                 g2.draw( tlo.getOutline( at ));
  671.             }
  672.             
  673.         /// ABP - restore old tform
  674.             g2.setTransform ( oldTx );
  675.             
  676.         }
  677.  
  678.         /// Draws one line of text at given position
  679.         private void tlDrawLine( Graphics2D g2, TextLayout tl,
  680.                                            float baseX, float baseY ) {
  681.         /// ABP - keep track of old tform, restore it later
  682.         AffineTransform oldTx = null;
  683.         oldTx = g2.getTransform();
  684.           g2.translate( baseX, baseY );              
  685.         g2.transform( getAffineTransform( g2Transform ) );
  686.     
  687.             tl.draw( g2, (float) 0, (float) 0 );
  688.  
  689.         /// ABP - restore old tform
  690.             g2.setTransform ( oldTx );
  691.             
  692.         }
  693.  
  694.  
  695.         /// If textToUse is set to range drawing, then convert
  696.         /// int to hex string and prepends 0s to make it length 4
  697.         /// Otherwise line number was fed; simply return number + 1 converted to String
  698.         /// (This is because first line is 1, not 0)
  699.         private String modeSpecificNumStr( int i ) {
  700.             if ( textToUse == USER_TEXT || textToUse == FILE_TEXT )
  701.               return String.valueOf( i + 1 );
  702.  
  703.             StringBuffer s = new StringBuffer( Integer.toHexString( i ));
  704.             while ( s.length() < 4 )
  705.               s.insert( 0, "0" );
  706.             return s.toString().toUpperCase();
  707.         }
  708.  
  709.         /// Resets the scrollbar to display correct range of text currently on screen
  710.         /// (This scrollbar is not part of a "ScrollPane". It merely simulates its effect by
  711.         ///  indicating the necessary area to be drawn within the panel.
  712.         ///  By doing this, it prevents creating gigantic panel when large text range,
  713.         ///  i.e. CJK Ideographs, is requested)
  714.         private void resetScrollbar( int oldValue ) {
  715.             int totalNumRows = 1, numCharToDisplay;
  716.             if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {
  717.                 if ( textToUse == RANGE_TEXT )
  718.                   numCharToDisplay = drawRange[1] - drawRange[0];
  719.                 else /// textToUse == ALL_GLYPHS
  720.                   numCharToDisplay = testFont.getNumGlyphs();
  721.  
  722.                 totalNumRows = numCharToDisplay / numCharAcross;
  723.                 if ( numCharToDisplay % numCharAcross != 0 )
  724.                   totalNumRows++;
  725.                 if ( oldValue / numCharAcross > totalNumRows )
  726.                   oldValue = 0;
  727.  
  728.                 verticalBar.setValues( oldValue / numCharAcross,
  729.                                        numCharDown, 0, totalNumRows );
  730.             }
  731.             else {
  732.                 if ( textToUse == USER_TEXT )
  733.                   totalNumRows = userText.length;
  734.                 else /// textToUse == FILE_TEXT;
  735.                   totalNumRows = lineBreakTLs.size();
  736.                 verticalBar.setValues( oldValue, numCharDown, 0, totalNumRows );
  737.             }
  738.             if ( totalNumRows <= numCharDown && drawStart == 0) {
  739.               verticalBar.setEnabled( false );
  740.             }
  741.             else {
  742.               verticalBar.setEnabled( true );
  743.             } 
  744.         }
  745.  
  746.         /// Calculates the font's metrics that will be used for draw
  747.         private void calcFontMetrics( Graphics2D g2d, int w, int h ) {
  748.             FontMetrics fm;
  749.             Graphics2D g2 = (Graphics2D)g2d.create();
  750.             
  751.             /// ABP
  752.             if ( g2Transform != NONE && textToUse != FILE_TEXT ) {
  753.                 g2.setFont( g2.getFont().deriveFont( getAffineTransform( g2Transform )) );
  754.                 fm = g2.getFontMetrics();
  755.             }
  756.             else {
  757.                 fm = g2.getFontMetrics();
  758.             }
  759.  
  760.             maxAscent = fm.getMaxAscent();
  761.             maxDescent = fm.getMaxDescent();
  762.  
  763.             if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {
  764.                 /// Give slight extra room for each character
  765.                 maxAscent += 3;
  766.                 maxDescent += 3;
  767.                 gridWidth = fm.getMaxAdvance() + 6;
  768.                 gridHeight = maxAscent + maxDescent;
  769.                 if ( force16Cols )
  770.                   numCharAcross = 16;
  771.                 else
  772.                   numCharAcross = ( w - 10 ) / gridWidth;
  773.                 numCharDown = ( h - 10 ) / gridHeight;
  774.  
  775.                 canvasInset_X = ( w - numCharAcross * gridWidth ) / 2;
  776.                 canvasInset_Y = ( h - numCharDown * gridHeight ) / 2;
  777.                 if ( numCharDown == 0 || numCharAcross == 0 )
  778.                   throw new CannotDrawException( isPrinting ? CANT_FIT_PRINT : CANT_FIT_DRAW );
  779.  
  780.                 if ( !isPrinting )
  781.                   resetScrollbar( verticalBar.getValue() * numCharAcross );
  782.             }
  783.             else {
  784.                 maxDescent += fm.getLeading();
  785.                 canvasInset_X = 5;
  786.                 canvasInset_Y = 5;
  787.                 /// gridWidth and numCharAcross will not be used in this mode...
  788.                 gridHeight = maxAscent + maxDescent;
  789.                 numCharDown = ( h - canvasInset_Y * 2 ) / gridHeight;
  790.  
  791.                 if ( numCharDown == 0 )
  792.                   throw new CannotDrawException( isPrinting ? CANT_FIT_PRINT : CANT_FIT_DRAW );
  793.                 /// If this is text loaded from file, prepares the LineBreak'ed
  794.                 /// text layout at this point
  795.                 if ( textToUse == FILE_TEXT ) {
  796.                     if ( !isPrinting )
  797.                       f2dt.fireChangeStatus( "LineBreaking Text... Please Wait", false );
  798.                     lineBreakTLs = new Vector();
  799.                     for ( int i = 0; i < fileText.length; i++ ) {
  800.                         AttributedString as =
  801.                           new AttributedString( fileText[i], g2.getFont().getAttributes() );
  802.  
  803.                         LineBreakMeasurer lbm =
  804.                           new LineBreakMeasurer( as.getIterator(), g2.getFontRenderContext() );
  805.  
  806.                         while ( lbm.getPosition() < fileText[i].length() )
  807.                           lineBreakTLs.add( lbm.nextLayout( (float) w ));
  808.  
  809.                     }
  810.                 }
  811.                 if ( !isPrinting )
  812.                   resetScrollbar( verticalBar.getValue() );
  813.             }
  814.         }
  815.  
  816.         /// Calculates the amount of text that will be displayed on screen
  817.         private void calcTextRange() {
  818.             String displaying = null;
  819.  
  820.             if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {
  821.                 if ( isPrinting )
  822.                   if ( printMode == ONE_PAGE )
  823.                     drawStart = currentlyShownChar;
  824.                   else /// printMode == CUR_RANGE
  825.                     drawStart = numCharAcross * numCharDown * printPageNumber;
  826.                 else
  827.                   drawStart = verticalBar.getValue() * numCharAcross;
  828.                 if ( textToUse == RANGE_TEXT ) {
  829.                     drawStart += drawRange[0];
  830.                     drawLimit = drawRange[1];
  831.                 }
  832.                 else
  833.                   drawLimit = testFont.getNumGlyphs();
  834.                 drawEnd = drawStart + numCharAcross * numCharDown - 1;
  835.  
  836.                 if ( drawEnd >= drawLimit )
  837.                   drawEnd = drawLimit;
  838.             }
  839.             else {
  840.                 if ( isPrinting )
  841.                   if ( printMode == ONE_PAGE )
  842.                     drawStart = currentlyShownChar;
  843.                   else /// printMode == ALL_TEXT
  844.                     drawStart = numCharDown * printPageNumber;
  845.                 else {
  846.                     drawStart = verticalBar.getValue();
  847.                 }
  848.  
  849.                 drawEnd = drawStart + numCharDown - 1;
  850.  
  851.                 if ( textToUse == USER_TEXT )
  852.                   drawLimit = userText.length - 1;
  853.                 else
  854.                   drawLimit = lineBreakTLs.size() - 1;
  855.  
  856.                 if ( drawEnd >= drawLimit )
  857.                   drawEnd = drawLimit;
  858.             }
  859.  
  860.             // ABP
  861.             if ( drawStart > drawEnd ) {
  862.               drawStart = 0;
  863.               verticalBar.setValue(drawStart);
  864.             }
  865.  
  866.  
  867.             /// Change the status bar if not printing...
  868.             if ( !isPrinting ) {
  869.                 backupStatusString = ( "Displaying" + MS_OPENING[textToUse] +
  870.                                        modeSpecificNumStr( drawStart ) + " to " +
  871.                                        modeSpecificNumStr( drawEnd ) +
  872.                                        MS_CLOSING[textToUse] );
  873.                 f2dt.fireChangeStatus( backupStatusString, false );
  874.             }
  875.         }
  876.  
  877.         /// Draws text according to the parameters set by Font2DTest GUI
  878.         private void drawText( Graphics g, int w, int h ) {
  879.             Graphics2D g2;
  880.  
  881.             /// Create back buffer when not printing, and its Graphics2D
  882.             /// Then set drawing parameters for that Graphics2D object
  883.             if ( isPrinting )
  884.               g2 = (Graphics2D) g;
  885.             else  {
  886.                 backBuffer = (BufferedImage) this.createImage( w, h );
  887.                 g2 = backBuffer.createGraphics();
  888.                 g2.setColor(Color.white);
  889.                 g2.fillRect(0, 0, w, h);
  890.                 g2.setColor(Color.black);
  891.             }
  892.             
  893.             /// sets font, RenderingHints, etc...
  894.             setParams( g2 );
  895.  
  896.             /// If flag is set, recalculate fontMetrics and reset the scrollbar
  897.             if ( updateFontMetrics || isPrinting ) {
  898.                 /// NOTE: re-calculates in case G2 transform
  899.                 /// is something other than NONE
  900.                 calcFontMetrics( g2, w, h );
  901.                 updateFontMetrics = false;
  902.             }
  903.             /// Calculate the amount of text that can be drawn...
  904.             calcTextRange();
  905.  
  906.             /// Draw according to the set "Text to Use" mode
  907.             if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {
  908.                 int charToDraw = drawStart;
  909.                 if ( showGrid )
  910.                   drawGrid( g2 );
  911.                 if ( !isPrinting )
  912.                   g.drawImage( backBuffer, 0, 0, this );
  913.  
  914.                 for ( int i = 0; i < numCharDown && charToDraw <= drawEnd; i++ ) {
  915.                   for ( int j = 0; j < numCharAcross && charToDraw <= drawEnd; j++, charToDraw++ ) {
  916.                       int gridLocX = j * gridWidth + canvasInset_X;
  917.                       int gridLocY = i * gridHeight + canvasInset_Y;
  918.  
  919.                       modeSpecificDrawChar( g2, charToDraw,
  920.                                             gridLocX + gridWidth / 2,
  921.                                             gridLocY + maxAscent );
  922.                       //if ( !isPrinting ) {
  923.                       //    g.setClip( gridLocX, gridLocY, gridWidth + 1, gridHeight + 1 );
  924.                       //    g.drawImage( backBuffer, 0, 0, this );
  925.                 //}
  926.  
  927.                   }
  928.                 }
  929.             }
  930.             else if ( textToUse == USER_TEXT ) {
  931.                 g2.drawRect( 0, 0, w - 1, h - 1 );
  932.                 if ( !isPrinting )
  933.                   g.drawImage( backBuffer, 0, 0, this );
  934.  
  935.                 for ( int i = drawStart; i <= drawEnd; i++ ) {
  936.                     int lineStartX = canvasInset_Y;
  937.                     int lineStartY = ( i - drawStart ) * gridHeight + maxAscent;
  938.                     modeSpecificDrawLine( g2, userText[i], lineStartX, lineStartY );
  939.                 }
  940.             }
  941.             else {
  942.                 float xPos, yPos = (float) canvasInset_Y;
  943.                 g2.drawRect( 0, 0, w - 1, h - 1 );
  944.                 if ( !isPrinting )
  945.                   g.drawImage( backBuffer, 0, 0, this );
  946.  
  947.                 for ( int i = drawStart; i <= drawEnd; i++ ) {
  948.                     TextLayout oneLine = (TextLayout) lineBreakTLs.elementAt( i );
  949.                     xPos =
  950.                       oneLine.isLeftToRight() ?
  951.                       canvasInset_X : ( (float) w - oneLine.getAdvance() - canvasInset_X );
  952.                     
  953.                     float fmData[] = {0, oneLine.getAscent(), 0, oneLine.getDescent(), 0, oneLine.getLeading()};
  954.                     if (g2Transform != NONE) {
  955.                         AffineTransform at = getAffineTransform(g2Transform);
  956.                         at.transform( fmData, 0, fmData, 0, 3);
  957.                     }
  958.                     //yPos += oneLine.getAscent();
  959.                     yPos += fmData[1]; // ascent
  960.                     //oneLine.draw( g2, xPos, yPos );
  961.                     tlDrawLine( g2, oneLine, xPos, yPos );
  962.                     //yPos += oneLine.getDescent() + oneLine.getLeading();
  963.                     yPos += fmData[3] + fmData[5]; // descent + leading
  964.                 }
  965.             }
  966.         if ( !isPrinting )
  967.                 g.drawImage( backBuffer, 0, 0, this );
  968.             g2.dispose();
  969.         }
  970.  
  971.         /// Component paintComponent function...
  972.         /// Draws/Refreshes canvas according to flag(s) set by other functions
  973.         public void paintComponent( Graphics g ) {
  974.             if ( updateBackBuffer ) {
  975.                 Dimension d = this.getSize();
  976.                 isPrinting = false;
  977.                 try {
  978.                     drawText( g, d.width, d.height );
  979.                 }
  980.                 catch ( CannotDrawException e ) {
  981.                     f2dt.fireChangeStatus( ERRORS[ e.id ], true );
  982.                     return;
  983.                 }
  984.             }
  985.             else {
  986.               /// Screen refresh
  987.               g.drawImage( backBuffer, 0, 0, this );
  988.             }
  989.  
  990.             showingError = false;
  991.             updateBackBuffer = false;
  992.         }
  993.  
  994.         /// Printable interface function
  995.         /// Component print function...
  996.         public int print( Graphics g, PageFormat pf, int pageIndex ) {
  997.             if ( pageIndex == 0 ) {
  998.                 /// Reset the last page index to max...
  999.                 lastPage = Integer.MAX_VALUE;
  1000.                 currentlyShownChar = verticalBar.getValue() * numCharAcross;
  1001.             }
  1002.  
  1003.             if ( printMode == ONE_PAGE ) {
  1004.                 if ( pageIndex > 0 )
  1005.                   return NO_SUCH_PAGE;
  1006.             }
  1007.             else {
  1008.                 if ( pageIndex > lastPage )
  1009.                   return NO_SUCH_PAGE;
  1010.             }
  1011.  
  1012.             int pageWidth = (int) pf.getImageableWidth();
  1013.             int pageHeight = (int) pf.getImageableHeight();
  1014.             /// Back up metrics and other drawing info before printing modifies it
  1015.             int backupDrawStart = drawStart, backupDrawEnd = drawEnd;
  1016.             int backupNumCharAcross = numCharAcross, backupNumCharDown = numCharDown;
  1017.             Vector backupLineBreakTLs = null;
  1018.             if ( textToUse == FILE_TEXT )
  1019.               backupLineBreakTLs = (Vector) lineBreakTLs.clone();
  1020.  
  1021.             printPageNumber = pageIndex;
  1022.             isPrinting = true;
  1023.             /// Push the actual draw area 60 down to allow info to be printed
  1024.             g.translate( (int) pf.getImageableX(), (int) pf.getImageableY() + 60 );
  1025.             try {
  1026.                 drawText( g, pageWidth, pageHeight - 60 );
  1027.             }
  1028.             catch ( CannotDrawException e ) {
  1029.                 f2dt.fireChangeStatus( ERRORS[ e.id ], true );
  1030.                 return NO_SUCH_PAGE;
  1031.             }
  1032.  
  1033.             /// Draw information about what is being printed
  1034.             String hints = ( " with antialias " + ( useAntialias ? "on" : "off" ) +
  1035.                              " and fractional metrics " + ( useFractional ? "on" : "off" ));
  1036.             String infoLine1 = ( "Printing" + MS_OPENING[textToUse] +
  1037.                                  modeSpecificNumStr( drawStart ) + " to " +
  1038.                                  modeSpecificNumStr( drawEnd ) + MS_CLOSING[textToUse] );
  1039.             String infoLine2 = ( "With " + fontName + " " + STYLES[fontStyle] + " at " +
  1040.                                  fontSize + " point size " + TRANSFORMS[fontTransform] );
  1041.             String infoLine3 = "Using " + METHODS[drawMethod] + hints;
  1042.             String infoLine4 = "Page: " + ( pageIndex + 1 );
  1043.             g.setFont( new Font( "dialog", Font.PLAIN, 12 ));
  1044.             g.setColor( Color.black );
  1045.             g.translate( 0, -60 );
  1046.             g.drawString( infoLine1, 15, 10 );
  1047.             g.drawString( infoLine2, 15, 22 );
  1048.             g.drawString( infoLine3, 15, 34 );
  1049.             g.drawString( infoLine4, 15, 46 );
  1050.  
  1051.             if ( drawEnd == drawLimit )
  1052.               /// This indicates that the draw will be completed with this page
  1053.               lastPage = pageIndex;
  1054.  
  1055.             /// Restore the changed values back...
  1056.             /// This is important for JScrollBar settings and LineBreak'ed TLs
  1057.             drawStart = backupDrawStart;
  1058.             drawEnd = backupDrawEnd;
  1059.             numCharAcross = backupNumCharAcross;
  1060.             numCharDown = backupNumCharDown;
  1061.             if ( textToUse == FILE_TEXT )
  1062.               lineBreakTLs = backupLineBreakTLs;
  1063.             return PAGE_EXISTS;
  1064.         }
  1065.  
  1066.         /// Ouputs the current canvas into a given JPEG file
  1067.         public void writeJPEG( String fileName ) {
  1068.             try {
  1069.                 BufferedOutputStream bos =
  1070.                   new BufferedOutputStream( new FileOutputStream( fileName ));
  1071.                 JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder( bos );
  1072.                 JPEGEncodeParam jep = encoder.getDefaultJPEGEncodeParam( backBuffer );
  1073.                 jep.setQuality( 1.0f, false );
  1074.                 encoder.setJPEGEncodeParam( jep );
  1075.                 encoder.encode( backBuffer );
  1076.                 bos.close();
  1077.             }
  1078.             catch ( Exception e ) {
  1079.                 f2dt.fireChangeStatus( "ERROR: Failed to Save JPEG image; See stack trace", true );
  1080.                 e.printStackTrace();
  1081.             }
  1082.         }
  1083.  
  1084.         /// Figures out whether a character at the pointer location is valid
  1085.         /// And if so, updates mouse location informations, as well as
  1086.         /// the information on the status bar
  1087.         private boolean checkMouseLoc( MouseEvent e ) {
  1088.             if ( gridWidth != 0 && gridHeight != 0 )
  1089.               if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {
  1090.                   int charLocX = ( e.getX() - canvasInset_X ) / gridWidth;
  1091.                   int charLocY = ( e.getY() - canvasInset_Y ) / gridHeight;
  1092.  
  1093.                   /// Check to make sure the mouse click location is within drawn area
  1094.                   if ( charLocX >= 0 && charLocY >= 0 &&
  1095.                        charLocX < numCharAcross && charLocY < numCharDown ) {
  1096.                       int mouseOverChar =
  1097.                         charLocX + ( verticalBar.getValue() + charLocY ) * numCharAcross;
  1098.                       if ( textToUse == RANGE_TEXT )
  1099.                         mouseOverChar += drawRange[0];
  1100.                       if ( mouseOverChar > drawEnd )
  1101.                         return false;
  1102.  
  1103.                       mouseOverCharX = charLocX;
  1104.                       mouseOverCharY = charLocY;
  1105.                       currMouseOverChar = mouseOverChar;
  1106.                       /// Update status bar
  1107.                       f2dt.fireChangeStatus( "Pointing to" + MS_OPENING[textToUse] +
  1108.                                              modeSpecificNumStr( mouseOverChar ), false );
  1109.                       return true;
  1110.                   }
  1111.               }
  1112.             return false;
  1113.         }
  1114.  
  1115.         /// Shows (updates) the character zoom window
  1116.         public void showZoomed() {
  1117.             GlyphVector gv;
  1118.             Font backup = testFont;
  1119.             Point canvasLoc = this.getLocationOnScreen();
  1120.  
  1121.             /// Calculate the zoom area's location and size...
  1122.             int dialogOffsetX = (int) ( gridWidth * ( ZOOM - 1 ) / 2 );
  1123.             int dialogOffsetY = (int) ( gridHeight * ( ZOOM - 1 ) / 2 );
  1124.             int zoomAreaX =
  1125.               mouseOverCharX * gridWidth + canvasInset_X - dialogOffsetX;
  1126.             int zoomAreaY =
  1127.               mouseOverCharY * gridHeight + canvasInset_Y - dialogOffsetY;
  1128.             int zoomAreaWidth = (int) ( gridWidth * ZOOM );
  1129.             int zoomAreaHeight = (int) ( gridHeight * ZOOM );
  1130.  
  1131.             /// Position and set size of zoom window as needed
  1132.             zoomWindow.setLocation( canvasLoc.x + zoomAreaX, canvasLoc.y + zoomAreaY );
  1133.             if ( !nowZooming ) {
  1134.                 if ( zoomWindow.getWarningString() != null )
  1135.                   /// If this is not opened as a "secure" window,
  1136.                   /// it has a banner below the zoom dialog which makes it look really BAD
  1137.                   /// So enlarge it by a bit
  1138.                   zoomWindow.setSize( zoomAreaWidth + 1, zoomAreaHeight + 20 );
  1139.                 else
  1140.                   zoomWindow.setSize( zoomAreaWidth + 1, zoomAreaHeight + 1 );
  1141.             }
  1142.  
  1143.             /// Prepare zoomed image
  1144.             zoomImage =
  1145.               (BufferedImage) zoomWindow.createImage( zoomAreaWidth + 1,
  1146.                                                       zoomAreaHeight + 1 );
  1147.             Graphics2D g2 = (Graphics2D) zoomImage.getGraphics();
  1148.             testFont = testFont.deriveFont( fontSize * ZOOM );
  1149.             setParams( g2 );
  1150.             g2.setColor( Color.white );
  1151.             g2.fillRect( 0, 0, zoomAreaWidth, zoomAreaHeight );
  1152.             g2.setColor( Color.black );
  1153.             g2.drawRect( 0, 0, zoomAreaWidth, zoomAreaHeight );
  1154.             modeSpecificDrawChar( g2, currMouseOverChar,
  1155.                                   zoomAreaWidth / 2, (int) ( maxAscent * ZOOM ));
  1156.             g2.dispose();
  1157.             if ( !nowZooming )
  1158.               zoomWindow.show();
  1159.             /// This is sort of redundant... since there is a paint function
  1160.             /// inside zoomWindow definition that does the drawImage.
  1161.             /// (I should be able to call just repaint() here)
  1162.             /// However, for some reason, that paint function fails to respond
  1163.             /// from second time and on; So I have to force the paint here...
  1164.             zoomWindow.getGraphics().drawImage( zoomImage, 0, 0, this );
  1165.  
  1166.             nowZooming = true;
  1167.             prevZoomChar = currMouseOverChar;
  1168.             testFont = backup;
  1169.             
  1170.             // Windows does not repaint correctly, after
  1171.             // a zoom. Thus, we need to force the canvas
  1172.             // to repaint, but only once. After the first repaint,
  1173.             // everything stabilizes. [ABP]
  1174.             if ( firstTime() ) {
  1175.                 refresh();
  1176.             }
  1177.         }
  1178.  
  1179.         /// Listener Functions
  1180.  
  1181.         /// MouseListener interface function
  1182.         /// Zooms a character when mouse is pressed above it
  1183.         public void mousePressed( MouseEvent e ) {
  1184.             if ( !showingError) {
  1185.                 if ( checkMouseLoc( e )) {
  1186.                     showZoomed();
  1187.                     this.setCursor( blankCursor );
  1188.                 }
  1189.             }
  1190.         }
  1191.  
  1192.         /// MouseListener interface function
  1193.         /// Redraws the area that was drawn over by zoomed character
  1194.         public void mouseReleased( MouseEvent e ) {
  1195.             if ( textToUse == RANGE_TEXT || textToUse == ALL_GLYPHS ) {
  1196.                 if ( nowZooming )
  1197.                   zoomWindow.hide();
  1198.                 nowZooming = false;
  1199.             }
  1200.             this.setCursor( Cursor.getDefaultCursor() );
  1201.         }
  1202.  
  1203.         /// MouseListener interface function
  1204.         /// Resets the status bar to display range instead of a specific character
  1205.         public void mouseExited( MouseEvent e ) {
  1206.             if ( !showingError && !nowZooming )
  1207.               f2dt.fireChangeStatus( backupStatusString, false );
  1208.         }
  1209.  
  1210.         /// MouseMotionListener interface function
  1211.         /// Adjusts the status bar message when mouse moves over a character
  1212.         public void mouseMoved( MouseEvent e ) {
  1213.             if ( !showingError ) {
  1214.                 if ( !checkMouseLoc( e ))
  1215.                   f2dt.fireChangeStatus( backupStatusString, false );
  1216.             }
  1217.         }
  1218.  
  1219.         /// MouseMotionListener interface function
  1220.         /// Scrolls the zoomed character when mouse is dragged
  1221.         public void mouseDragged( MouseEvent e ) {
  1222.             if ( !showingError )
  1223.               if ( nowZooming ) {
  1224.                   if ( checkMouseLoc( e ) && currMouseOverChar != prevZoomChar )
  1225.                     showZoomed();
  1226.               }
  1227.         }
  1228.  
  1229.         /// Empty function to comply with interface requirement
  1230.         public void mouseClicked( MouseEvent e ) {}
  1231.         public void mouseEntered( MouseEvent e ) {}
  1232.     }
  1233.  
  1234.     private final class CannotDrawException extends RuntimeException {
  1235.         /// Error ID
  1236.         public final int id;
  1237.  
  1238.         public CannotDrawException( int i ) {
  1239.             id = i;
  1240.         }
  1241.     }
  1242. }
  1243.