1 /*
   2  * Copyright (c) 1999, 2018, Oracle and/or its affiliates. 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  *   - Redistributions in binary form must reproduce the above copyright
  12  *     notice, this list of conditions and the following disclaimer in the
  13  *     documentation and/or other materials provided with the distribution.
  14  *
  15  *   - Neither the name of Oracle nor the names of its
  16  *     contributors may be used to endorse or promote products derived
  17  *     from this software without specific prior written permission.
  18  *
  19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30  */
  31 
  32 /*
  33  * This source code is provided to illustrate the usage of a given feature
  34  * or technique and has been deliberately simplified. Additional steps
  35  * required for a production-quality application, such as security checks,
  36  * input validation and proper error handling, might not be present in
  37  * this sample code.
  38  */
  39 
  40 
  41 
  42 import java.awt.Color;
  43 import java.awt.Component;
  44 import java.awt.BorderLayout;
  45 import java.awt.CheckboxGroup;
  46 import java.awt.Container;
  47 import java.awt.Dimension;
  48 import java.awt.Font;
  49 import java.awt.Graphics;
  50 import java.awt.Graphics2D;
  51 import java.awt.GraphicsEnvironment;
  52 import java.awt.GridBagConstraints;
  53 import java.awt.GridBagLayout;
  54 import java.awt.GridLayout;
  55 import java.awt.Insets;
  56 import java.awt.RenderingHints;
  57 import java.awt.Toolkit;
  58 import java.awt.event.ActionEvent;
  59 import java.awt.event.ActionListener;
  60 import java.awt.event.ItemEvent;
  61 import java.awt.event.ItemListener;
  62 import java.awt.event.WindowAdapter;
  63 import java.awt.event.WindowEvent;
  64 import java.awt.image.BufferedImage;
  65 import java.io.BufferedInputStream;
  66 import java.io.BufferedOutputStream;
  67 import java.io.File;
  68 import java.io.FileInputStream;
  69 import java.io.FileOutputStream;
  70 import java.util.StringTokenizer;
  71 import java.util.BitSet;
  72 import javax.swing.*;
  73 import javax.swing.event.*;
  74 
  75 /**
  76  * Font2DTest.java
  77  *
  78  * @author Shinsuke Fukuda
  79  * @author Ankit Patel [Conversion to Swing - 01/07/30]
  80  */
  81 
  82 /// Main Font2DTest Class
  83 
  84 public final class Font2DTest extends JPanel
  85     implements ActionListener, ItemListener, ChangeListener {
  86 
  87     /// JFrame that will contain Font2DTest
  88     private final JFrame parent;
  89     /// FontPanel class that will contain all graphical output
  90     private final FontPanel fp;
  91     /// RangeMenu class that contains info about the unicode ranges
  92     private final RangeMenu rm;
  93 
  94     /// Other menus to set parameters for text drawing
  95     private final ChoiceV2 fontMenu;
  96     private final JTextField sizeField;
  97     private final ChoiceV2 styleMenu;
  98     private final ChoiceV2 textMenu;
  99     private int currentTextChoice = 0;
 100     private final ChoiceV2 transformMenu;
 101     private final ChoiceV2 transformMenuG2;
 102     private final ChoiceV2 methodsMenu;
 103     private final JComboBox<FontPanel.AAValues> antiAliasMenu;
 104     private final JComboBox<FontPanel.FMValues> fracMetricsMenu;
 105 
 106     private final JSlider contrastSlider;
 107 
 108     /// CheckboxMenuItems
 109     private CheckboxMenuItemV2 displayGridCBMI;
 110     private CheckboxMenuItemV2 force16ColsCBMI;
 111     private CheckboxMenuItemV2 showFontInfoCBMI;
 112 
 113     /// JDialog boxes
 114     private JDialog userTextDialog;
 115     private JTextArea userTextArea;
 116     private JDialog printDialog;
 117     private JDialog fontInfoDialog;
 118     private LabelV2[] fontInfos = new LabelV2[2];
 119     private JFileChooser filePromptDialog = null;
 120 
 121     private ButtonGroup printCBGroup;
 122     private JRadioButton[] printModeCBs = new JRadioButton[3];
 123 
 124     /// Status bar
 125     private final LabelV2 statusBar;
 126 
 127     private int[] fontStyles  = {Font.PLAIN, Font.BOLD, Font.ITALIC, Font.BOLD | Font.ITALIC};
 128 
 129     /// Text filename
 130     private String tFileName;
 131 
 132     // Enabled or disabled status of canDisplay check
 133     private static boolean canDisplayCheck = true;
 134 
 135     /// Initialize GUI variables and its layouts
 136     public Font2DTest( JFrame f, boolean isApplet ) {
 137         parent = f;
 138 
 139         rm = new RangeMenu( this, parent );
 140         fp = new FontPanel( this, parent );
 141         statusBar = new LabelV2("");
 142 
 143         fontMenu = new ChoiceV2( this, canDisplayCheck );
 144         sizeField = new JTextField( "12", 3 );
 145         sizeField.addActionListener( this );
 146         styleMenu = new ChoiceV2( this );
 147         textMenu = new ChoiceV2( ); // listener added later
 148         transformMenu = new ChoiceV2( this );
 149         transformMenuG2 = new ChoiceV2( this );
 150         methodsMenu = new ChoiceV2( this );
 151 
 152         antiAliasMenu =
 153             new JComboBox<>(FontPanel.AAValues.values());
 154         antiAliasMenu.addActionListener(this);
 155         fracMetricsMenu =
 156             new JComboBox<>(FontPanel.FMValues.values());
 157         fracMetricsMenu.addActionListener(this);
 158 
 159         contrastSlider = new JSlider(JSlider.HORIZONTAL, 100, 250,
 160                                  FontPanel.getDefaultLCDContrast().intValue());
 161         contrastSlider.setEnabled(false);
 162         contrastSlider.setMajorTickSpacing(20);
 163         contrastSlider.setMinorTickSpacing(10);
 164         contrastSlider.setPaintTicks(true);
 165         contrastSlider.setPaintLabels(true);
 166         contrastSlider.addChangeListener(this);
 167         setupPanel();
 168         setupMenu( isApplet );
 169         setupDialog( isApplet );
 170 
 171         if(canDisplayCheck) {
 172             fireRangeChanged();
 173         }
 174     }
 175 
 176     /// Set up the main interface panel
 177     private void setupPanel() {
 178         GridBagLayout gbl = new GridBagLayout();
 179         GridBagConstraints gbc = new GridBagConstraints();
 180         gbc.fill = GridBagConstraints.HORIZONTAL;
 181         gbc.weightx = 1;
 182         gbc.insets = new Insets( 2, 0, 2, 2 );
 183         this.setLayout( gbl );
 184 
 185         addLabeledComponentToGBL( "Font: ", fontMenu, gbl, gbc, this );
 186         addLabeledComponentToGBL( "Size: ", sizeField, gbl, gbc, this );
 187         gbc.gridwidth = GridBagConstraints.REMAINDER;
 188         addLabeledComponentToGBL( "Font Transform:",
 189                                   transformMenu, gbl, gbc, this );
 190         gbc.gridwidth = 1;
 191 
 192         addLabeledComponentToGBL( "Range: ", rm, gbl, gbc, this );
 193         addLabeledComponentToGBL( "Style: ", styleMenu, gbl, gbc, this );
 194         gbc.gridwidth = GridBagConstraints.REMAINDER;
 195         addLabeledComponentToGBL( "Graphics Transform: ",
 196                                   transformMenuG2, gbl, gbc, this );
 197         gbc.gridwidth = 1;
 198 
 199         gbc.anchor = GridBagConstraints.WEST;
 200         addLabeledComponentToGBL( "Method: ", methodsMenu, gbl, gbc, this );
 201         addLabeledComponentToGBL("", null, gbl, gbc, this);
 202         gbc.anchor = GridBagConstraints.EAST;
 203         gbc.gridwidth = GridBagConstraints.REMAINDER;
 204         addLabeledComponentToGBL( "Text to use:", textMenu, gbl, gbc, this );
 205 
 206         gbc.weightx=1;
 207         gbc.gridwidth = 1;
 208         gbc.fill = GridBagConstraints.HORIZONTAL;
 209         gbc.anchor = GridBagConstraints.WEST;
 210         addLabeledComponentToGBL("LCD contrast: ",
 211                                   contrastSlider, gbl, gbc, this);
 212 
 213         gbc.gridwidth = 1;
 214         gbc.fill = GridBagConstraints.NONE;
 215         addLabeledComponentToGBL("Antialiasing: ",
 216                                   antiAliasMenu, gbl, gbc, this);
 217 
 218         gbc.anchor = GridBagConstraints.EAST;
 219         gbc.gridwidth = GridBagConstraints.REMAINDER;
 220         addLabeledComponentToGBL("Fractional metrics: ",
 221                                   fracMetricsMenu, gbl, gbc, this);
 222 
 223         gbc.weightx = 1;
 224         gbc.weighty = 1;
 225         gbc.anchor = GridBagConstraints.WEST;
 226         gbc.insets = new Insets( 2, 0, 0, 2 );
 227         gbc.fill = GridBagConstraints.BOTH;
 228         gbl.setConstraints( fp, gbc );
 229         this.add( fp );
 230 
 231         gbc.weighty = 0;
 232         gbc.insets = new Insets( 0, 2, 0, 0 );
 233         gbl.setConstraints( statusBar, gbc );
 234         this.add( statusBar );
 235     }
 236 
 237     /// Adds a component to a container with a label to its left in GridBagLayout
 238     private void addLabeledComponentToGBL( String name,
 239                                            JComponent c,
 240                                            GridBagLayout gbl,
 241                                            GridBagConstraints gbc,
 242                                            Container target ) {
 243         LabelV2 l = new LabelV2( name );
 244         GridBagConstraints gbcLabel = (GridBagConstraints) gbc.clone();
 245         gbcLabel.insets = new Insets( 2, 2, 2, 0 );
 246         gbcLabel.gridwidth = 1;
 247         gbcLabel.weightx = 0;
 248 
 249         if ( c == null )
 250           c = new JLabel( "" );
 251 
 252         gbl.setConstraints( l, gbcLabel );
 253         target.add( l );
 254         gbl.setConstraints( c, gbc );
 255         target.add( c );
 256     }
 257 
 258     /// Sets up menu entries
 259     private void setupMenu( boolean isApplet ) {
 260         JMenu fileMenu = new JMenu( "File" );
 261         JMenu optionMenu = new JMenu( "Option" );
 262 
 263         fileMenu.add( new MenuItemV2( "Save Selected Options...", this ));
 264         fileMenu.add( new MenuItemV2( "Load Options...", this ));
 265         fileMenu.addSeparator();
 266         fileMenu.add( new MenuItemV2( "Save as PNG...", this ));
 267         fileMenu.add( new MenuItemV2( "Load PNG File to Compare...", this ));
 268         fileMenu.add( new MenuItemV2( "Page Setup...", this ));
 269         fileMenu.add( new MenuItemV2( "Print...", this ));
 270         fileMenu.addSeparator();
 271         if ( !isApplet )
 272           fileMenu.add( new MenuItemV2( "Exit", this ));
 273         else
 274           fileMenu.add( new MenuItemV2( "Close", this ));
 275 
 276         displayGridCBMI = new CheckboxMenuItemV2( "Display Grid", true, this );
 277         force16ColsCBMI = new CheckboxMenuItemV2( "Force 16 Columns", false, this );
 278         showFontInfoCBMI = new CheckboxMenuItemV2( "Display Font Info", false, this );
 279         optionMenu.add( displayGridCBMI );
 280         optionMenu.add( force16ColsCBMI );
 281         optionMenu.add( showFontInfoCBMI );
 282 
 283         JMenuBar mb = parent.getJMenuBar();
 284         if ( mb == null )
 285           mb = new JMenuBar();
 286         mb.add( fileMenu );
 287         mb.add( optionMenu );
 288 
 289         parent.setJMenuBar( mb );
 290 
 291         String[] fontList =
 292           GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
 293 
 294         for ( int i = 0; i < fontList.length; i++ )
 295           fontMenu.addItem( fontList[i] );
 296         fontMenu.setSelectedItem( "Dialog" );
 297 
 298         styleMenu.addItem( "Plain" );
 299         styleMenu.addItem( "Bold" );
 300         styleMenu.addItem( "Italic" );
 301         styleMenu.addItem( "Bold Italic" );
 302 
 303         transformMenu.addItem( "None" );
 304         transformMenu.addItem( "Scale" );
 305         transformMenu.addItem( "Shear" );
 306         transformMenu.addItem( "Rotate" );
 307 
 308         transformMenuG2.addItem( "None" );
 309         transformMenuG2.addItem( "Scale" );
 310         transformMenuG2.addItem( "Shear" );
 311         transformMenuG2.addItem( "Rotate" );
 312 
 313         methodsMenu.addItem( "drawString" );
 314         methodsMenu.addItem( "drawChars" );
 315         methodsMenu.addItem( "drawBytes" );
 316         methodsMenu.addItem( "drawGlyphVector" );
 317         methodsMenu.addItem( "TextLayout.draw" );
 318         methodsMenu.addItem( "GlyphVector.getOutline + draw" );
 319         methodsMenu.addItem( "TextLayout.getOutline + draw" );
 320 
 321         textMenu.addItem( "Unicode Range" );
 322         textMenu.addItem( "All Glyphs" );
 323         textMenu.addItem( "User Text" );
 324         textMenu.addItem( "Text File" );
 325         textMenu.addActionListener ( this ); // listener added later so unneeded events not thrown
 326     }
 327 
 328     /// Sets up the all dialogs used in Font2DTest...
 329     private void setupDialog( boolean isApplet ) {
 330         if (!isApplet)
 331                 filePromptDialog = new JFileChooser( );
 332         else
 333                 filePromptDialog = null;
 334 
 335         /// Prepare user text dialog...
 336         userTextDialog = new JDialog( parent, "User Text", false );
 337         JPanel dialogTopPanel = new JPanel();
 338         JPanel dialogBottomPanel = new JPanel();
 339         LabelV2 message1 = new LabelV2( "Enter text below and then press update" );
 340         LabelV2 message2 = new LabelV2( "(Unicode char can be denoted by \\uXXXX)" );
 341         LabelV2 message3 = new LabelV2( "(Supplementary chars can be denoted by \\UXXXXXX)" );
 342         userTextArea = new JTextArea( "Font2DTest!" );
 343         ButtonV2 bUpdate = new ButtonV2( "Update", this );
 344         userTextArea.setFont( new Font( "dialog", Font.PLAIN, 12 ));
 345         dialogTopPanel.setLayout( new GridLayout( 3, 1 ));
 346         dialogTopPanel.add( message1 );
 347         dialogTopPanel.add( message2 );
 348         dialogTopPanel.add( message3 );
 349         dialogBottomPanel.add( bUpdate );
 350         //ABP
 351         JScrollPane userTextAreaSP = new JScrollPane(userTextArea);
 352         userTextAreaSP.setPreferredSize(new Dimension(300, 100));
 353 
 354         userTextDialog.getContentPane().setLayout( new BorderLayout() );
 355         userTextDialog.getContentPane().add( "North", dialogTopPanel );
 356         userTextDialog.getContentPane().add( "Center", userTextAreaSP );
 357         userTextDialog.getContentPane().add( "South", dialogBottomPanel );
 358         userTextDialog.pack();
 359         userTextDialog.addWindowListener( new WindowAdapter() {
 360             public void windowClosing( WindowEvent e ) {
 361                 userTextDialog.setVisible(false);
 362             }
 363         });
 364 
 365         /// Prepare printing dialog...
 366         printCBGroup = new ButtonGroup();
 367         printModeCBs[ fp.ONE_PAGE ] =
 368           new JRadioButton( "Print one page from currently displayed character/line",
 369                          true );
 370         printModeCBs[ fp.CUR_RANGE ] =
 371           new JRadioButton( "Print all characters in currently selected range",
 372                          false );
 373         printModeCBs[ fp.ALL_TEXT ] =
 374           new JRadioButton( "Print all lines of text",
 375                          false );
 376         LabelV2 l =
 377           new LabelV2( "Note: Page range in native \"Print\" dialog will not affect the result" );
 378         JPanel buttonPanel = new JPanel();
 379         printModeCBs[ fp.ALL_TEXT ].setEnabled( false );
 380         buttonPanel.add( new ButtonV2( "Print", this ));
 381         buttonPanel.add( new ButtonV2( "Cancel", this ));
 382 
 383         printDialog = new JDialog( parent, "Print...", true );
 384         printDialog.setResizable( false );
 385         printDialog.addWindowListener( new WindowAdapter() {
 386             public void windowClosing( WindowEvent e ) {
 387                 printDialog.setVisible(false);
 388             }
 389         });
 390         printDialog.getContentPane().setLayout( new GridLayout( printModeCBs.length + 2, 1 ));
 391         printDialog.getContentPane().add( l );
 392         for ( int i = 0; i < printModeCBs.length; i++ ) {
 393             printCBGroup.add( printModeCBs[i] );
 394             printDialog.getContentPane().add( printModeCBs[i] );
 395         }
 396         printDialog.getContentPane().add( buttonPanel );
 397         printDialog.pack();
 398 
 399         /// Prepare font information dialog...
 400         fontInfoDialog = new JDialog( parent, "Font info", false );
 401         fontInfoDialog.setResizable( false );
 402         fontInfoDialog.addWindowListener( new WindowAdapter() {
 403             public void windowClosing( WindowEvent e ) {
 404                 fontInfoDialog.setVisible(false);
 405                 showFontInfoCBMI.setState( false );
 406             }
 407         });
 408         JPanel fontInfoPanel = new JPanel();
 409         fontInfoPanel.setLayout( new GridLayout( fontInfos.length, 1 ));
 410         for ( int i = 0; i < fontInfos.length; i++ ) {
 411             fontInfos[i] = new LabelV2("");
 412             fontInfoPanel.add( fontInfos[i] );
 413         }
 414         fontInfoDialog.getContentPane().add( fontInfoPanel );
 415 
 416         /// Move the location of the dialog...
 417         userTextDialog.setLocation( 200, 300 );
 418         fontInfoDialog.setLocation( 0, 400 );
 419     }
 420 
 421     /// RangeMenu object signals using this function
 422     /// when Unicode range has been changed and text needs to be redrawn
 423     public void fireRangeChanged() {
 424         int[] range = rm.getSelectedRange();
 425         fp.setTextToDraw( fp.RANGE_TEXT, range, null, null );
 426         if(canDisplayCheck) {
 427             setupFontList(range[0], range[1]);
 428         }
 429         if ( showFontInfoCBMI.getState() )
 430           fireUpdateFontInfo();
 431     }
 432 
 433     /// Changes the message on the status bar
 434     public void fireChangeStatus( String message, boolean error ) {
 435         /// If this is not ran as an applet, use own status bar,
 436         /// Otherwise, use the appletviewer/browser's status bar
 437         statusBar.setText( message );
 438         if ( error )
 439           fp.showingError = true;
 440         else
 441           fp.showingError = false;
 442     }
 443 
 444     /// Updates the information about the selected font
 445     public void fireUpdateFontInfo() {
 446         if ( showFontInfoCBMI.getState() ) {
 447             String[] infos = fp.getFontInfo();
 448             for ( int i = 0; i < fontInfos.length; i++ )
 449               fontInfos[i].setText( infos[i] );
 450             fontInfoDialog.pack();
 451         }
 452     }
 453 
 454     private void setupFontList(int rangeStart, int rangeEnd) {
 455 
 456         int listCount = fontMenu.getItemCount();
 457         int size = 16;
 458 
 459         try {
 460             size =  Float.valueOf(sizeField.getText()).intValue();
 461         }
 462         catch ( Exception e ) {
 463             System.out.println("Invalid font size in the size textField. Using default value of 16");
 464         }
 465 
 466         int style = fontStyles[styleMenu.getSelectedIndex()];
 467         Font f;
 468         for (int i = 0; i < listCount; i++) {
 469             String fontName = fontMenu.getItemAt(i);
 470             f = new Font(fontName, style, size);
 471             if ((rm.getSelectedIndex() != RangeMenu.SURROGATES_AREA_INDEX) &&
 472                 canDisplayRange(f, rangeStart, rangeEnd)) {
 473                 fontMenu.setBit(i, true);
 474             }
 475             else {
 476                 fontMenu.setBit(i, false);
 477             }
 478         }
 479 
 480         fontMenu.repaint();
 481     }
 482 
 483     protected boolean canDisplayRange(Font font, int rangeStart, int rangeEnd) {
 484         for (int i = rangeStart; i < rangeEnd; i++) {
 485             if (font.canDisplay(i)) {
 486                 return true;
 487             }
 488         }
 489         return false;
 490     }
 491 
 492     /// Displays a file load/save dialog and returns the specified file
 493     private String promptFile( boolean isSave, String initFileName ) {
 494         int retVal;
 495         String str;
 496 
 497         /// ABP
 498         if ( filePromptDialog == null)
 499                 return null;
 500 
 501         if ( isSave ) {
 502             filePromptDialog.setDialogType( JFileChooser.SAVE_DIALOG );
 503             filePromptDialog.setDialogTitle( "Save..." );
 504             str = "Save";
 505 
 506 
 507         }
 508         else {
 509             filePromptDialog.setDialogType( JFileChooser.OPEN_DIALOG );
 510             filePromptDialog.setDialogTitle( "Load..." );
 511             str = "Load";
 512         }
 513 
 514         if (initFileName != null)
 515                 filePromptDialog.setSelectedFile( new File( initFileName ) );
 516         retVal = filePromptDialog.showDialog( this, str );
 517 
 518         if ( retVal == JFileChooser.APPROVE_OPTION ) {
 519                 File file = filePromptDialog.getSelectedFile();
 520                 String fileName = file.getAbsolutePath();
 521                 if ( fileName != null ) {
 522                         return fileName;
 523                 }
 524         }
 525 
 526         return null;
 527     }
 528 
 529     /// Converts user text into arrays of String, delimited at newline character
 530     /// Also replaces any valid escape sequence with appropriate unicode character
 531     /// Support \\UXXXXXX notation for surrogates
 532     private String[] parseUserText( String orig ) {
 533         int length = orig.length();
 534         StringTokenizer perLine = new StringTokenizer( orig, "\n" );
 535         String[] textLines = new String[ perLine.countTokens() ];
 536         int lineNumber = 0;
 537 
 538         while ( perLine.hasMoreElements() ) {
 539             StringBuffer converted = new StringBuffer();
 540             String oneLine = perLine.nextToken();
 541             int lineLength = oneLine.length();
 542             int prevEscapeEnd = 0;
 543             int nextEscape = -1;
 544             do {
 545                 int nextBMPEscape = oneLine.indexOf( "\\u", prevEscapeEnd );
 546                 int nextSupEscape = oneLine.indexOf( "\\U", prevEscapeEnd );
 547                 nextEscape = (nextBMPEscape < 0)
 548                     ? ((nextSupEscape < 0)
 549                        ? -1
 550                        : nextSupEscape)
 551                     : ((nextSupEscape < 0)
 552                        ? nextBMPEscape
 553                        : Math.min(nextBMPEscape, nextSupEscape));
 554 
 555                 if ( nextEscape != -1 ) {
 556                     if ( prevEscapeEnd < nextEscape )
 557                         converted.append( oneLine.substring( prevEscapeEnd, nextEscape ));
 558 
 559                     prevEscapeEnd = nextEscape + (nextEscape == nextBMPEscape ? 6 : 8);
 560                     try {
 561                         String hex = oneLine.substring( nextEscape + 2, prevEscapeEnd );
 562                         if (nextEscape == nextBMPEscape) {
 563                             converted.append( (char) Integer.parseInt( hex, 16 ));
 564                         } else {
 565                             converted.append( new String( Character.toChars( Integer.parseInt( hex, 16 ))));
 566                         }
 567                     }
 568                     catch ( Exception e ) {
 569                         int copyLimit = Math.min(lineLength, prevEscapeEnd);
 570                         converted.append( oneLine.substring( nextEscape, copyLimit ));
 571                     }
 572                 }
 573             } while (nextEscape != -1);
 574             if ( prevEscapeEnd < lineLength )
 575               converted.append( oneLine.substring( prevEscapeEnd, lineLength ));
 576             textLines[ lineNumber++ ] = converted.toString();
 577         }
 578         return textLines;
 579     }
 580 
 581     /// Reads the text from specified file, detecting UTF-16 encoding
 582     /// Then breaks the text into String array, delimited at every line break
 583     private void readTextFile( String fileName ) {
 584         try {
 585             String fileText;
 586             String[] textLines;
 587             BufferedInputStream bis =
 588               new BufferedInputStream( new FileInputStream( fileName ));
 589             int numBytes = bis.available();
 590             if (numBytes == 0) {
 591                 throw new Exception("Text file " + fileName + " is empty");
 592             }
 593             byte[] byteData = new byte[ numBytes ];
 594             bis.read( byteData, 0, numBytes );
 595             bis.close();
 596 
 597             /// If byte mark is found, then use UTF-16 encoding to convert bytes...
 598             if (numBytes >= 2 &&
 599                 (( byteData[0] == (byte) 0xFF && byteData[1] == (byte) 0xFE ) ||
 600                  ( byteData[0] == (byte) 0xFE && byteData[1] == (byte) 0xFF )))
 601               fileText = new String( byteData, "UTF-16" );
 602             /// Otherwise, use system default encoding
 603             else
 604               fileText = new String( byteData );
 605 
 606             int length = fileText.length();
 607             StringTokenizer perLine = new StringTokenizer( fileText, "\n" );
 608             /// Determine "Return Char" used in this file
 609             /// This simply finds first occurrence of CR, CR+LF or LF...
 610             for ( int i = 0; i < length; i++ ) {
 611                 char iTh = fileText.charAt( i );
 612                 if ( iTh == '\r' ) {
 613                     if ( i < length - 1 && fileText.charAt( i + 1 ) == '\n' )
 614                       perLine = new StringTokenizer( fileText, "\r\n" );
 615                     else
 616                       perLine = new StringTokenizer( fileText, "\r" );
 617                     break;
 618                 }
 619                 else if ( iTh == '\n' )
 620                   /// Use the one already created
 621                   break;
 622             }
 623             int lineNumber = 0, numLines = perLine.countTokens();
 624             textLines = new String[ numLines ];
 625 
 626             while ( perLine.hasMoreElements() ) {
 627                 String oneLine = perLine.nextToken();
 628                 if ( oneLine == null )
 629                   /// To make LineBreakMeasurer to return a valid TextLayout
 630                   /// on an empty line, simply feed it a space char...
 631                   oneLine = " ";
 632                 textLines[ lineNumber++ ] = oneLine;
 633             }
 634             fp.setTextToDraw( fp.FILE_TEXT, null, null, textLines );
 635             rm.setEnabled( false );
 636             methodsMenu.setEnabled( false );
 637         }
 638         catch ( Exception ex ) {
 639             fireChangeStatus( "ERROR: Failed to Read Text File; See Stack Trace", true );
 640             ex.printStackTrace();
 641         }
 642     }
 643 
 644     /// Returns a String storing current configuration
 645     private void writeCurrentOptions( String fileName ) {
 646         try {
 647             String curOptions = fp.getCurrentOptions();
 648             BufferedOutputStream bos =
 649               new BufferedOutputStream( new FileOutputStream( fileName ));
 650             /// Prepend title and the option that is only obtainable here
 651             int[] range = rm.getSelectedRange();
 652             String completeOptions =
 653               ( "Font2DTest Option File\n" +
 654                 displayGridCBMI.getState() + "\n" +
 655                 force16ColsCBMI.getState() + "\n" +
 656                 showFontInfoCBMI.getState() + "\n" +
 657                 rm.getSelectedItem() + "\n" +
 658                 range[0] + "\n" + range[1] + "\n" + curOptions + tFileName);
 659             byte[] toBeWritten = completeOptions.getBytes( "UTF-16" );
 660             bos.write( toBeWritten, 0, toBeWritten.length );
 661             bos.close();
 662         }
 663         catch ( Exception ex ) {
 664             fireChangeStatus( "ERROR: Failed to Save Options File; See Stack Trace", true );
 665             ex.printStackTrace();
 666         }
 667     }
 668 
 669     /// Updates GUI visibility/status after some parameters have changed
 670     private void updateGUI() {
 671         int selectedText = textMenu.getSelectedIndex();
 672 
 673         /// Set the visibility of User Text dialog
 674         if ( selectedText == fp.USER_TEXT )
 675           userTextDialog.setVisible(true);
 676         else
 677           userTextDialog.setVisible(false);
 678         /// Change the visibility/status/availability of Print JDialog buttons
 679         printModeCBs[ fp.ONE_PAGE ].setSelected( true );
 680         if ( selectedText == fp.FILE_TEXT || selectedText == fp.USER_TEXT ) {
 681             /// ABP
 682             /// update methodsMenu to show that TextLayout.draw is being used
 683             /// when we are in FILE_TEXT mode
 684             if ( selectedText == fp.FILE_TEXT )
 685                 methodsMenu.setSelectedItem("TextLayout.draw");
 686             methodsMenu.setEnabled( selectedText == fp.USER_TEXT );
 687             printModeCBs[ fp.CUR_RANGE ].setEnabled( false );
 688             printModeCBs[ fp.ALL_TEXT ].setEnabled( true );
 689         }
 690         else {
 691             /// ABP
 692             /// update methodsMenu to show that drawGlyph is being used
 693             /// when we are in ALL_GLYPHS mode
 694             if ( selectedText == fp.ALL_GLYPHS )
 695                 methodsMenu.setSelectedItem("drawGlyphVector");
 696             methodsMenu.setEnabled( selectedText == fp.RANGE_TEXT );
 697             printModeCBs[ fp.CUR_RANGE ].setEnabled( true );
 698             printModeCBs[ fp.ALL_TEXT ].setEnabled( false );
 699         }
 700         /// Modify RangeMenu and fontInfo label availabilty
 701         if ( selectedText == fp.RANGE_TEXT ) {
 702             fontInfos[1].setVisible( true );
 703             rm.setEnabled( true );
 704         }
 705         else {
 706             fontInfos[1].setVisible( false );
 707             rm.setEnabled( false );
 708         }
 709     }
 710 
 711     /// Loads saved options and applies them
 712     private void loadOptions( String fileName ) {
 713         try {
 714             BufferedInputStream bis =
 715               new BufferedInputStream( new FileInputStream( fileName ));
 716             int numBytes = bis.available();
 717             byte[] byteData = new byte[ numBytes ];
 718             bis.read( byteData, 0, numBytes );
 719             bis.close();
 720             if ( numBytes < 2 ||
 721                 (byteData[0] != (byte) 0xFE || byteData[1] != (byte) 0xFF) )
 722               throw new Exception( "Not a Font2DTest options file" );
 723 
 724             String options = new String( byteData, "UTF-16" );
 725             StringTokenizer perLine = new StringTokenizer( options, "\n" );
 726             String title = perLine.nextToken();
 727             if ( !title.equals( "Font2DTest Option File" ))
 728               throw new Exception( "Not a Font2DTest options file" );
 729 
 730             /// Parse all options
 731             boolean displayGridOpt = Boolean.parseBoolean( perLine.nextToken() );
 732             boolean force16ColsOpt = Boolean.parseBoolean( perLine.nextToken() );
 733             boolean showFontInfoOpt = Boolean.parseBoolean( perLine.nextToken() );
 734             String rangeNameOpt = perLine.nextToken();
 735             int rangeStartOpt = Integer.parseInt( perLine.nextToken() );
 736             int rangeEndOpt = Integer.parseInt( perLine.nextToken() );
 737             String fontNameOpt = perLine.nextToken();
 738             float fontSizeOpt = Float.parseFloat( perLine.nextToken() );
 739             int fontStyleOpt = Integer.parseInt( perLine.nextToken() );
 740             int fontTransformOpt = Integer.parseInt( perLine.nextToken() );
 741             int g2TransformOpt = Integer.parseInt( perLine.nextToken() );
 742             int textToUseOpt = Integer.parseInt( perLine.nextToken() );
 743             int drawMethodOpt = Integer.parseInt( perLine.nextToken() );
 744             int antialiasOpt = Integer.parseInt(perLine.nextToken());
 745             int fractionalOpt = Integer.parseInt(perLine.nextToken());
 746             int lcdContrast = Integer.parseInt(perLine.nextToken());
 747             String[] userTextOpt = { "Font2DTest!" };
 748             String dialogEntry = "Font2DTest!";
 749             if (textToUseOpt == fp.USER_TEXT )  {
 750                 int numLines = perLine.countTokens(), lineNumber = 0;
 751                 if ( numLines != 0 ) {
 752                     userTextOpt = new String[ numLines ];
 753                     dialogEntry = "";
 754                     for ( ; perLine.hasMoreElements(); lineNumber++ ) {
 755                         userTextOpt[ lineNumber ] = perLine.nextToken();
 756                         dialogEntry += userTextOpt[ lineNumber ] + "\n";
 757                     }
 758                 }
 759             }
 760 
 761             /// Reset GUIs
 762             displayGridCBMI.setState( displayGridOpt );
 763             force16ColsCBMI.setState( force16ColsOpt );
 764             showFontInfoCBMI.setState( showFontInfoOpt );
 765             rm.setSelectedRange( rangeNameOpt, rangeStartOpt, rangeEndOpt );
 766             fontMenu.setSelectedItem( fontNameOpt );
 767             sizeField.setText( String.valueOf( fontSizeOpt ));
 768             styleMenu.setSelectedIndex( fontStyleOpt );
 769             transformMenu.setSelectedIndex( fontTransformOpt );
 770             transformMenuG2.setSelectedIndex( g2TransformOpt );
 771             textMenu.setSelectedIndex( textToUseOpt );
 772             methodsMenu.setSelectedIndex( drawMethodOpt );
 773             antiAliasMenu.setSelectedIndex( antialiasOpt );
 774             fracMetricsMenu.setSelectedIndex( fractionalOpt );
 775             contrastSlider.setValue(lcdContrast);
 776 
 777             userTextArea.setText( dialogEntry );
 778             updateGUI();
 779 
 780             if ( textToUseOpt == fp.FILE_TEXT ) {
 781               tFileName = perLine.nextToken();
 782               readTextFile(tFileName );
 783             }
 784 
 785             /// Reset option variables and repaint
 786             fp.loadOptions( displayGridOpt, force16ColsOpt,
 787                             rangeStartOpt, rangeEndOpt,
 788                             fontNameOpt, fontSizeOpt,
 789                             fontStyleOpt, fontTransformOpt, g2TransformOpt,
 790                             textToUseOpt, drawMethodOpt,
 791                             antialiasOpt, fractionalOpt,
 792                             lcdContrast, userTextOpt );
 793             if ( showFontInfoOpt ) {
 794                 fireUpdateFontInfo();
 795                 fontInfoDialog.setVisible(true);
 796             }
 797             else
 798               fontInfoDialog.setVisible(false);
 799         }
 800         catch ( Exception ex ) {
 801             fireChangeStatus( "ERROR: Failed to Load Options File; See Stack Trace", true );
 802             ex.printStackTrace();
 803         }
 804     }
 805 
 806     /// Loads a previously saved image
 807     private void loadComparisonPNG( String fileName ) {
 808         try {
 809             BufferedImage image =
 810                 javax.imageio.ImageIO.read(new File(fileName));
 811             JFrame f = new JFrame( "Comparison PNG" );
 812             ImagePanel ip = new ImagePanel( image );
 813             f.setResizable( false );
 814             f.getContentPane().add( ip );
 815             f.addWindowListener( new WindowAdapter() {
 816                 public void windowClosing( WindowEvent e ) {
 817                     ( (JFrame) e.getSource() ).dispose();
 818                 }
 819             });
 820             f.pack();
 821             f.setVisible(true);
 822         }
 823         catch ( Exception ex ) {
 824             fireChangeStatus( "ERROR: Failed to Load PNG File; See Stack Trace", true );
 825             ex.printStackTrace();
 826         }
 827     }
 828 
 829     /// Interface functions...
 830 
 831     /// ActionListener interface function
 832     /// Responds to JMenuItem, JTextField and JButton actions
 833     public void actionPerformed( ActionEvent e ) {
 834         Object source = e.getSource();
 835 
 836         if ( source instanceof JMenuItem ) {
 837             JMenuItem mi = (JMenuItem) source;
 838             String itemName = mi.getText();
 839 
 840             if ( itemName.equals( "Save Selected Options..." )) {
 841                 String fileName = promptFile( true, "options.txt" );
 842                 if ( fileName != null )
 843                   writeCurrentOptions( fileName );
 844             }
 845             else if ( itemName.equals( "Load Options..." )) {
 846                 String fileName = promptFile( false, "options.txt" );
 847                 if ( fileName != null )
 848                   loadOptions( fileName );
 849             }
 850             else if ( itemName.equals( "Save as PNG..." )) {
 851                 String fileName = promptFile( true, fontMenu.getSelectedItem() + ".png" );
 852                 if ( fileName != null )
 853                   fp.doSavePNG( fileName );
 854             }
 855             else if ( itemName.equals( "Load PNG File to Compare..." )) {
 856                 String fileName = promptFile( false, null );
 857                 if ( fileName != null )
 858                   loadComparisonPNG( fileName );
 859             }
 860             else if ( itemName.equals( "Page Setup..." ))
 861               fp.doPageSetup();
 862             else if ( itemName.equals( "Print..." ))
 863               printDialog.setVisible(true);
 864             else if ( itemName.equals( "Close" ))
 865               parent.dispose();
 866             else if ( itemName.equals( "Exit" ))
 867               System.exit(0);
 868         }
 869 
 870         else if ( source instanceof JTextField ) {
 871             JTextField tf = (JTextField) source;
 872             float sz = 12f;
 873             try {
 874                  sz = Float.parseFloat(sizeField.getText());
 875                  if (sz < 1f || sz > 120f) {
 876                       sz = 12f;
 877                       sizeField.setText("12");
 878                  }
 879             } catch (Exception se) {
 880                  sizeField.setText("12");
 881             }
 882             if ( tf == sizeField )
 883               fp.setFontParams( fontMenu.getSelectedItem(),
 884                                 sz,
 885                                 styleMenu.getSelectedIndex(),
 886                                 transformMenu.getSelectedIndex() );
 887         }
 888 
 889         else if ( source instanceof JButton ) {
 890             String itemName = ( (JButton) source ).getText();
 891             /// Print dialog buttons...
 892             if ( itemName.equals( "Print" )) {
 893                 for ( int i = 0; i < printModeCBs.length; i++ )
 894                   if ( printModeCBs[i].isSelected() ) {
 895                       printDialog.setVisible(false);
 896                       fp.doPrint( i );
 897                   }
 898             }
 899             else if ( itemName.equals( "Cancel" ))
 900               printDialog.setVisible(false);
 901             /// Update button from Usert Text JDialog...
 902             else if ( itemName.equals( "Update" ))
 903               fp.setTextToDraw( fp.USER_TEXT, null,
 904                                 parseUserText( userTextArea.getText() ), null );
 905         }
 906         else if ( source instanceof JComboBox ) {
 907             JComboBox<?> c = (JComboBox<?>) source;
 908 
 909             /// RangeMenu handles actions by itself and then calls fireRangeChanged,
 910             /// so it is not listed or handled here
 911             if ( c == fontMenu || c == styleMenu || c == transformMenu ) {
 912                 float sz = 12f;
 913                 try {
 914                     sz = Float.parseFloat(sizeField.getText());
 915                     if (sz < 1f || sz > 120f) {
 916                         sz = 12f;
 917                         sizeField.setText("12");
 918                     }
 919                 } catch (Exception se) {
 920                     sizeField.setText("12");
 921                 }
 922                 fp.setFontParams(fontMenu.getSelectedItem(),
 923                                  sz,
 924                                  styleMenu.getSelectedIndex(),
 925                                  transformMenu.getSelectedIndex());
 926             } else if ( c == methodsMenu )
 927               fp.setDrawMethod( methodsMenu.getSelectedIndex() );
 928             else if ( c == textMenu ) {
 929 
 930                 if(canDisplayCheck) {
 931                     fireRangeChanged();
 932                 }
 933 
 934                 int selected = textMenu.getSelectedIndex();
 935 
 936                 if ( selected == fp.RANGE_TEXT )
 937                   fp.setTextToDraw( fp.RANGE_TEXT, rm.getSelectedRange(),
 938                                     null, null );
 939                 else if ( selected == fp.USER_TEXT )
 940                   fp.setTextToDraw( fp.USER_TEXT, null,
 941                                     parseUserText( userTextArea.getText() ), null );
 942                 else if ( selected == fp.FILE_TEXT ) {
 943                     String fileName = promptFile( false, null );
 944                     if ( fileName != null ) {
 945                       tFileName = fileName;
 946                       readTextFile( fileName );
 947                     } else {
 948                         /// User cancelled selection; reset to previous choice
 949                         c.setSelectedIndex( currentTextChoice );
 950                         return;
 951                     }
 952                 }
 953                 else if ( selected == fp.ALL_GLYPHS )
 954                   fp.setTextToDraw( fp.ALL_GLYPHS, null, null, null );
 955 
 956                 updateGUI();
 957                 currentTextChoice = selected;
 958             }
 959             else if ( c == transformMenuG2 ) {
 960                 fp.setTransformG2( transformMenuG2.getSelectedIndex() );
 961             }
 962             else if (c == antiAliasMenu || c == fracMetricsMenu) {
 963                 if (c == antiAliasMenu) {
 964                     boolean enabled = FontPanel.AAValues.
 965                         isLCDMode(antiAliasMenu.getSelectedItem());
 966                         contrastSlider.setEnabled(enabled);
 967                 }
 968                 fp.setRenderingHints(antiAliasMenu.getSelectedItem(),
 969                                      fracMetricsMenu.getSelectedItem(),
 970                                      contrastSlider.getValue());
 971             }
 972         }
 973     }
 974 
 975     public void stateChanged(ChangeEvent e) {
 976          Object source = e.getSource();
 977          if (source instanceof JSlider) {
 978              fp.setRenderingHints(antiAliasMenu.getSelectedItem(),
 979                                   fracMetricsMenu.getSelectedItem(),
 980                                   contrastSlider.getValue());
 981          }
 982     }
 983 
 984     /// ItemListener interface function
 985     /// Responds to JCheckBoxMenuItem, JComboBox and JCheckBox actions
 986     public void itemStateChanged( ItemEvent e ) {
 987         Object source = e.getSource();
 988 
 989         if ( source instanceof JCheckBoxMenuItem ) {
 990             JCheckBoxMenuItem cbmi = (JCheckBoxMenuItem) source;
 991             if ( cbmi == displayGridCBMI )
 992               fp.setGridDisplay( displayGridCBMI.getState() );
 993             else if ( cbmi == force16ColsCBMI )
 994               fp.setForce16Columns( force16ColsCBMI.getState() );
 995             else if ( cbmi == showFontInfoCBMI ) {
 996                 if ( showFontInfoCBMI.getState() ) {
 997                     fireUpdateFontInfo();
 998                     fontInfoDialog.setVisible(true);
 999                 }
1000                 else
1001                   fontInfoDialog.setVisible(false);
1002             }
1003         }
1004     }
1005 
1006     private static void printUsage() {
1007         String usage = "Usage: java -jar Font2DTest.jar [options]\n" +
1008             "\nwhere options include:\n" +
1009             "    -dcdc | -disablecandisplaycheck disable canDisplay check for font\n" +
1010             "    -?    | -help                   print this help message\n" +
1011             "\nExample :\n" +
1012             "     To disable canDisplay check on font for ranges\n" +
1013             "     java -jar Font2DTest.jar -dcdc";
1014         System.out.println(usage);
1015         System.exit(0);
1016     }
1017 
1018     /// Main function
1019     public static void main(String[] argv) {
1020 
1021         if(argv.length > 0) {
1022             if(argv[0].equalsIgnoreCase("-disablecandisplaycheck") ||
1023                argv[0].equalsIgnoreCase("-dcdc")) {
1024                 canDisplayCheck = false;
1025             }
1026             else {
1027                 printUsage();
1028             }
1029         }
1030 
1031         UIManager.put("swing.boldMetal", Boolean.FALSE);
1032         final JFrame f = new JFrame( "Font2DTest" );
1033         final Font2DTest f2dt = new Font2DTest( f, false );
1034         f.addWindowListener( new WindowAdapter() {
1035             public void windowOpening( WindowEvent e ) { f2dt.repaint(); }
1036             public void windowClosing( WindowEvent e ) { System.exit(0); }
1037         });
1038 
1039         f.getContentPane().add( f2dt );
1040         f.pack();
1041         f.setVisible(true);
1042     }
1043 
1044     /// Inner class definitions...
1045 
1046     /// Class to display just an image file
1047     /// Used to show the comparison PNG image
1048     private final class ImagePanel extends JPanel {
1049         private final BufferedImage bi;
1050 
1051         public ImagePanel( BufferedImage image ) {
1052             bi = image;
1053         }
1054 
1055         public Dimension getPreferredSize() {
1056             return new Dimension( bi.getWidth(), bi.getHeight() );
1057         }
1058 
1059         public void paintComponent( Graphics g ) {
1060             g.drawImage( bi, 0, 0, this );
1061         }
1062     }
1063 
1064     /// Classes made to avoid repetitive calls... (being lazy)
1065     private final class ButtonV2 extends JButton {
1066         public ButtonV2( String name, ActionListener al ) {
1067             super( name );
1068             this.addActionListener( al );
1069         }
1070     }
1071 
1072     private final class ChoiceV2 extends JComboBox<String> {
1073 
1074         private BitSet bitSet = null;
1075 
1076         public ChoiceV2() {;}
1077 
1078         public ChoiceV2( ActionListener al ) {
1079             super();
1080             this.addActionListener( al );
1081         }
1082 
1083         public ChoiceV2( ActionListener al, boolean fontChoice) {
1084             this(al);
1085             if(fontChoice) {
1086                 //Register this component in ToolTipManager
1087                 setToolTipText("");
1088                 bitSet = new BitSet();
1089                 setRenderer(new ChoiceV2Renderer(this));
1090             }
1091         }
1092 
1093         public String getToolTipText() {
1094             int index = this.getSelectedIndex();
1095             String fontName = (String) this.getSelectedItem();
1096             if(fontName != null &&
1097                (textMenu.getSelectedIndex() == fp.RANGE_TEXT)) {
1098                 if (getBit(index)) {
1099                     return "Font \"" + fontName + "\" can display some characters in \"" +
1100                         rm.getSelectedItem() + "\" range";
1101                 }
1102                 else {
1103                     return "Font \"" + fontName + "\" cannot display any characters in \"" +
1104                         rm.getSelectedItem() + "\" range";
1105                 }
1106             }
1107             return super.getToolTipText();
1108         }
1109 
1110         public void setBit(int bitIndex, boolean value) {
1111             bitSet.set(bitIndex, value);
1112         }
1113 
1114         public boolean getBit(int bitIndex) {
1115             return bitSet.get(bitIndex);
1116         }
1117     }
1118 
1119     private final class ChoiceV2Renderer extends DefaultListCellRenderer {
1120 
1121         private ImageIcon yesImage, blankImage;
1122         private ChoiceV2 choice = null;
1123 
1124         public ChoiceV2Renderer(ChoiceV2 choice) {
1125             BufferedImage yes =
1126                 new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB);
1127             Graphics2D g = yes.createGraphics();
1128             g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1129                                RenderingHints.VALUE_ANTIALIAS_ON);
1130             g.setColor(Color.BLUE);
1131             g.drawLine(0, 5, 3, 10);
1132             g.drawLine(1, 5, 4, 10);
1133             g.drawLine(3, 10, 10, 0);
1134             g.drawLine(4, 9, 9, 0);
1135             g.dispose();
1136             BufferedImage blank =
1137                 new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB);
1138             yesImage = new ImageIcon(yes);
1139             blankImage = new ImageIcon(blank);
1140             this.choice = choice;
1141         }
1142 
1143         public Component getListCellRendererComponent(JList<?> list,
1144                                                       Object value,
1145                                                       int index,
1146                                                       boolean isSelected,
1147                                                       boolean cellHasFocus) {
1148 
1149             if(textMenu.getSelectedIndex() == fp.RANGE_TEXT) {
1150 
1151                 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
1152 
1153                 //For JComboBox if index is -1, its rendering the selected index.
1154                 if(index == -1) {
1155                     index = choice.getSelectedIndex();
1156                 }
1157 
1158                 if(choice.getBit(index)) {
1159                     setIcon(yesImage);
1160                 }
1161                 else {
1162                     setIcon(blankImage);
1163                 }
1164 
1165             } else {
1166                 super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
1167                 setIcon(blankImage);
1168             }
1169 
1170             return this;
1171         }
1172     }
1173 
1174     private final class LabelV2 extends JLabel {
1175         public LabelV2( String name ) {
1176             super( name );
1177         }
1178     }
1179 
1180     private final class MenuItemV2 extends JMenuItem {
1181         public MenuItemV2( String name, ActionListener al ) {
1182             super( name );
1183             this.addActionListener( al );
1184         }
1185     }
1186 
1187     private final class CheckboxMenuItemV2 extends JCheckBoxMenuItem {
1188         public CheckboxMenuItemV2( String name, boolean b, ItemListener il ) {
1189             super( name, b );
1190             this.addItemListener( il );
1191         }
1192     }
1193 }