1 /*
2  * Copyright (c) 1999, 2011, 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.EnumSet;
71 import java.util.StringTokenizer;
72 import java.util.BitSet;
73 import javax.swing.*;
74 import javax.swing.event.*;
75 
76 /**
77  * Font2DTest.java
78  *
79  * @author Shinsuke Fukuda
80  * @author Ankit Patel [Conversion to Swing - 01/07/30]
81  */
82 
83 /// Main Font2DTest Class
84 
85 public final class Font2DTest extends JPanel
86     implements ActionListener, ItemListener, ChangeListener {
87 
88     /// JFrame that will contain Font2DTest
89     private final JFrame parent;
90     /// FontPanel class that will contain all graphical output
91     private final FontPanel fp;
92     /// RangeMenu class that contains info about the unicode ranges
93     private final RangeMenu rm;
94 
95     /// Other menus to set parameters for text drawing
96     private final ChoiceV2 fontMenu;
97     private final JTextField sizeField;
98     private final ChoiceV2 styleMenu;
99     private final ChoiceV2 textMenu;
100     private int currentTextChoice = 0;
101     private final ChoiceV2 transformMenu;
102     private final ChoiceV2 transformMenuG2;
103     private final ChoiceV2 methodsMenu;
104     private final JComboBox antiAliasMenu;
105     private final JComboBox fracMetricsMenu;
106 
107     private final JSlider contrastSlider;
108 
109     /// CheckboxMenuItems
110     private CheckboxMenuItemV2 displayGridCBMI;
111     private CheckboxMenuItemV2 force16ColsCBMI;
112     private CheckboxMenuItemV2 showFontInfoCBMI;
113 
114     /// JDialog boxes
115     private JDialog userTextDialog;
116     private JTextArea userTextArea;
117     private JDialog printDialog;
118     private JDialog fontInfoDialog;
119     private LabelV2 fontInfos[] = new LabelV2[2];
120     private JFileChooser filePromptDialog = null;
121 
122     private ButtonGroup printCBGroup;
123     private JRadioButton printModeCBs[] = new JRadioButton[3];
124 
125     /// Status bar
126     private final LabelV2 statusBar;
127 
128     private int fontStyles [] = {Font.PLAIN, Font.BOLD, Font.ITALIC, Font.BOLD | Font.ITALIC};
129 
130     /// Text filename
131     private String tFileName;
132 
133     // Enabled or disabled status of canDisplay check
134     private static boolean canDisplayCheck = true;
135 
136     /// Initialize GUI variables and its layouts
Font2DTest( JFrame f, boolean isApplet )137     public Font2DTest( JFrame f, boolean isApplet ) {
138         parent = f;
139 
140         rm = new RangeMenu( this, parent );
141         fp = new FontPanel( this, parent );
142         statusBar = new LabelV2("");
143 
144         fontMenu = new ChoiceV2( this, canDisplayCheck );
145         sizeField = new JTextField( "12", 3 );
146         sizeField.addActionListener( this );
147         styleMenu = new ChoiceV2( this );
148         textMenu = new ChoiceV2( ); // listener added later
149         transformMenu = new ChoiceV2( this );
150         transformMenuG2 = new ChoiceV2( this );
151         methodsMenu = new ChoiceV2( this );
152 
153         antiAliasMenu =
154             new JComboBox(EnumSet.allOf(FontPanel.AAValues.class).toArray());
155         antiAliasMenu.addActionListener(this);
156         fracMetricsMenu =
157             new JComboBox(EnumSet.allOf(FontPanel.FMValues.class).toArray());
158         fracMetricsMenu.addActionListener(this);
159 
160         contrastSlider = new JSlider(JSlider.HORIZONTAL, 100, 250,
161                                  FontPanel.getDefaultLCDContrast().intValue());
162         contrastSlider.setEnabled(false);
163         contrastSlider.setMajorTickSpacing(20);
164         contrastSlider.setMinorTickSpacing(10);
165         contrastSlider.setPaintTicks(true);
166         contrastSlider.setPaintLabels(true);
167         contrastSlider.addChangeListener(this);
168         setupPanel();
169         setupMenu( isApplet );
170         setupDialog( isApplet );
171 
172         if(canDisplayCheck) {
173             fireRangeChanged();
174         }
175     }
176 
177     /// Set up the main interface panel
setupPanel()178     private void setupPanel() {
179         GridBagLayout gbl = new GridBagLayout();
180         GridBagConstraints gbc = new GridBagConstraints();
181         gbc.fill = GridBagConstraints.HORIZONTAL;
182         gbc.weightx = 1;
183         gbc.insets = new Insets( 2, 0, 2, 2 );
184         this.setLayout( gbl );
185 
186         addLabeledComponentToGBL( "Font: ", fontMenu, gbl, gbc, this );
187         addLabeledComponentToGBL( "Size: ", sizeField, gbl, gbc, this );
188         gbc.gridwidth = GridBagConstraints.REMAINDER;
189         addLabeledComponentToGBL( "Font Transform:",
190                                   transformMenu, gbl, gbc, this );
191         gbc.gridwidth = 1;
192 
193         addLabeledComponentToGBL( "Range: ", rm, gbl, gbc, this );
194         addLabeledComponentToGBL( "Style: ", styleMenu, gbl, gbc, this );
195         gbc.gridwidth = GridBagConstraints.REMAINDER;
196         addLabeledComponentToGBL( "Graphics Transform: ",
197                                   transformMenuG2, gbl, gbc, this );
198         gbc.gridwidth = 1;
199 
200         gbc.anchor = GridBagConstraints.WEST;
201         addLabeledComponentToGBL( "Method: ", methodsMenu, gbl, gbc, this );
202         addLabeledComponentToGBL("", null, gbl, gbc, this);
203         gbc.anchor = GridBagConstraints.EAST;
204         gbc.gridwidth = GridBagConstraints.REMAINDER;
205         addLabeledComponentToGBL( "Text to use:", textMenu, gbl, gbc, this );
206 
207         gbc.weightx=1;
208         gbc.gridwidth = 1;
209         gbc.fill = GridBagConstraints.HORIZONTAL;
210         gbc.anchor = GridBagConstraints.WEST;
211         addLabeledComponentToGBL("LCD contrast: ",
212                                   contrastSlider, gbl, gbc, this);
213 
214         gbc.gridwidth = 1;
215         gbc.fill = GridBagConstraints.NONE;
216         addLabeledComponentToGBL("Antialiasing: ",
217                                   antiAliasMenu, gbl, gbc, this);
218 
219         gbc.anchor = GridBagConstraints.EAST;
220         gbc.gridwidth = GridBagConstraints.REMAINDER;
221         addLabeledComponentToGBL("Fractional metrics: ",
222                                   fracMetricsMenu, gbl, gbc, this);
223 
224         gbc.weightx = 1;
225         gbc.weighty = 1;
226         gbc.anchor = GridBagConstraints.WEST;
227         gbc.insets = new Insets( 2, 0, 0, 2 );
228         gbc.fill = GridBagConstraints.BOTH;
229         gbl.setConstraints( fp, gbc );
230         this.add( fp );
231 
232         gbc.weighty = 0;
233         gbc.insets = new Insets( 0, 2, 0, 0 );
234         gbl.setConstraints( statusBar, gbc );
235         this.add( statusBar );
236     }
237 
238     /// Adds a component to a container with a label to its left in GridBagLayout
addLabeledComponentToGBL( String name, JComponent c, GridBagLayout gbl, GridBagConstraints gbc, Container target )239     private void addLabeledComponentToGBL( String name,
240                                            JComponent c,
241                                            GridBagLayout gbl,
242                                            GridBagConstraints gbc,
243                                            Container target ) {
244         LabelV2 l = new LabelV2( name );
245         GridBagConstraints gbcLabel = (GridBagConstraints) gbc.clone();
246         gbcLabel.insets = new Insets( 2, 2, 2, 0 );
247         gbcLabel.gridwidth = 1;
248         gbcLabel.weightx = 0;
249 
250         if ( c == null )
251           c = new JLabel( "" );
252 
253         gbl.setConstraints( l, gbcLabel );
254         target.add( l );
255         gbl.setConstraints( c, gbc );
256         target.add( c );
257     }
258 
259     /// Sets up menu entries
setupMenu( boolean isApplet )260     private void setupMenu( boolean isApplet ) {
261         JMenu fileMenu = new JMenu( "File" );
262         JMenu optionMenu = new JMenu( "Option" );
263 
264         fileMenu.add( new MenuItemV2( "Save Selected Options...", this ));
265         fileMenu.add( new MenuItemV2( "Load Options...", this ));
266         fileMenu.addSeparator();
267         fileMenu.add( new MenuItemV2( "Save as PNG...", this ));
268         fileMenu.add( new MenuItemV2( "Load PNG File to Compare...", this ));
269         fileMenu.add( new MenuItemV2( "Page Setup...", this ));
270         fileMenu.add( new MenuItemV2( "Print...", this ));
271         fileMenu.addSeparator();
272         if ( !isApplet )
273           fileMenu.add( new MenuItemV2( "Exit", this ));
274         else
275           fileMenu.add( new MenuItemV2( "Close", this ));
276 
277         displayGridCBMI = new CheckboxMenuItemV2( "Display Grid", true, this );
278         force16ColsCBMI = new CheckboxMenuItemV2( "Force 16 Columns", false, this );
279         showFontInfoCBMI = new CheckboxMenuItemV2( "Display Font Info", false, this );
280         optionMenu.add( displayGridCBMI );
281         optionMenu.add( force16ColsCBMI );
282         optionMenu.add( showFontInfoCBMI );
283 
284         JMenuBar mb = parent.getJMenuBar();
285         if ( mb == null )
286           mb = new JMenuBar();
287         mb.add( fileMenu );
288         mb.add( optionMenu );
289 
290         parent.setJMenuBar( mb );
291 
292         String fontList[] =
293           GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
294 
295         for ( int i = 0; i < fontList.length; i++ )
296           fontMenu.addItem( fontList[i] );
297         fontMenu.setSelectedItem( "Dialog" );
298 
299         styleMenu.addItem( "Plain" );
300         styleMenu.addItem( "Bold" );
301         styleMenu.addItem( "Italic" );
302         styleMenu.addItem( "Bold Italic" );
303 
304         transformMenu.addItem( "None" );
305         transformMenu.addItem( "Scale" );
306         transformMenu.addItem( "Shear" );
307         transformMenu.addItem( "Rotate" );
308 
309         transformMenuG2.addItem( "None" );
310         transformMenuG2.addItem( "Scale" );
311         transformMenuG2.addItem( "Shear" );
312         transformMenuG2.addItem( "Rotate" );
313 
314         methodsMenu.addItem( "drawString" );
315         methodsMenu.addItem( "drawChars" );
316         methodsMenu.addItem( "drawBytes" );
317         methodsMenu.addItem( "drawGlyphVector" );
318         methodsMenu.addItem( "TextLayout.draw" );
319         methodsMenu.addItem( "GlyphVector.getOutline + draw" );
320         methodsMenu.addItem( "TextLayout.getOutline + draw" );
321 
322         textMenu.addItem( "Unicode Range" );
323         textMenu.addItem( "All Glyphs" );
324         textMenu.addItem( "User Text" );
325         textMenu.addItem( "Text File" );
326         textMenu.addActionListener ( this ); // listener added later so unneeded events not thrown
327     }
328 
329     /// Sets up the all dialogs used in Font2DTest...
setupDialog( boolean isApplet )330     private void setupDialog( boolean isApplet ) {
331         if (!isApplet)
332                 filePromptDialog = new JFileChooser( );
333         else
334                 filePromptDialog = null;
335 
336         /// Prepare user text dialog...
337         userTextDialog = new JDialog( parent, "User Text", false );
338         JPanel dialogTopPanel = new JPanel();
339         JPanel dialogBottomPanel = new JPanel();
340         LabelV2 message1 = new LabelV2( "Enter text below and then press update" );
341         LabelV2 message2 = new LabelV2( "(Unicode char can be denoted by \\uXXXX)" );
342         LabelV2 message3 = new LabelV2( "(Supplementary chars can be denoted by \\UXXXXXX)" );
343         userTextArea = new JTextArea( "Font2DTest!" );
344         ButtonV2 bUpdate = new ButtonV2( "Update", this );
345         userTextArea.setFont( new Font( "dialog", Font.PLAIN, 12 ));
346         dialogTopPanel.setLayout( new GridLayout( 3, 1 ));
347         dialogTopPanel.add( message1 );
348         dialogTopPanel.add( message2 );
349         dialogTopPanel.add( message3 );
350         dialogBottomPanel.add( bUpdate );
351         //ABP
352         JScrollPane userTextAreaSP = new JScrollPane(userTextArea);
353         userTextAreaSP.setPreferredSize(new Dimension(300, 100));
354 
355         userTextDialog.getContentPane().setLayout( new BorderLayout() );
356         userTextDialog.getContentPane().add( "North", dialogTopPanel );
357         userTextDialog.getContentPane().add( "Center", userTextAreaSP );
358         userTextDialog.getContentPane().add( "South", dialogBottomPanel );
359         userTextDialog.pack();
360         userTextDialog.addWindowListener( new WindowAdapter() {
361             public void windowClosing( WindowEvent e ) {
362                 userTextDialog.hide();
363             }
364         });
365 
366         /// Prepare printing dialog...
367         printCBGroup = new ButtonGroup();
368         printModeCBs[ fp.ONE_PAGE ] =
369           new JRadioButton( "Print one page from currently displayed character/line",
370                          true );
371         printModeCBs[ fp.CUR_RANGE ] =
372           new JRadioButton( "Print all characters in currently selected range",
373                          false );
374         printModeCBs[ fp.ALL_TEXT ] =
375           new JRadioButton( "Print all lines of text",
376                          false );
377         LabelV2 l =
378           new LabelV2( "Note: Page range in native \"Print\" dialog will not affect the result" );
379         JPanel buttonPanel = new JPanel();
380         printModeCBs[ fp.ALL_TEXT ].setEnabled( false );
381         buttonPanel.add( new ButtonV2( "Print", this ));
382         buttonPanel.add( new ButtonV2( "Cancel", this ));
383 
384         printDialog = new JDialog( parent, "Print...", true );
385         printDialog.setResizable( false );
386         printDialog.addWindowListener( new WindowAdapter() {
387             public void windowClosing( WindowEvent e ) {
388                 printDialog.hide();
389             }
390         });
391         printDialog.getContentPane().setLayout( new GridLayout( printModeCBs.length + 2, 1 ));
392         printDialog.getContentPane().add( l );
393         for ( int i = 0; i < printModeCBs.length; i++ ) {
394             printCBGroup.add( printModeCBs[i] );
395             printDialog.getContentPane().add( printModeCBs[i] );
396         }
397         printDialog.getContentPane().add( buttonPanel );
398         printDialog.pack();
399 
400         /// Prepare font information dialog...
401         fontInfoDialog = new JDialog( parent, "Font info", false );
402         fontInfoDialog.setResizable( false );
403         fontInfoDialog.addWindowListener( new WindowAdapter() {
404             public void windowClosing( WindowEvent e ) {
405                 fontInfoDialog.hide();
406                 showFontInfoCBMI.setState( false );
407             }
408         });
409         JPanel fontInfoPanel = new JPanel();
410         fontInfoPanel.setLayout( new GridLayout( fontInfos.length, 1 ));
411         for ( int i = 0; i < fontInfos.length; i++ ) {
412             fontInfos[i] = new LabelV2("");
413             fontInfoPanel.add( fontInfos[i] );
414         }
415         fontInfoDialog.getContentPane().add( fontInfoPanel );
416 
417         /// Move the location of the dialog...
418         userTextDialog.setLocation( 200, 300 );
419         fontInfoDialog.setLocation( 0, 400 );
420     }
421 
422     /// RangeMenu object signals using this function
423     /// when Unicode range has been changed and text needs to be redrawn
fireRangeChanged()424     public void fireRangeChanged() {
425         int range[] = rm.getSelectedRange();
426         fp.setTextToDraw( fp.RANGE_TEXT, range, null, null );
427         if(canDisplayCheck) {
428             setupFontList(range[0], range[1]);
429         }
430         if ( showFontInfoCBMI.getState() )
431           fireUpdateFontInfo();
432     }
433 
434     /// Changes the message on the status bar
fireChangeStatus( String message, boolean error )435     public void fireChangeStatus( String message, boolean error ) {
436         /// If this is not ran as an applet, use own status bar,
437         /// Otherwise, use the appletviewer/browser's status bar
438         statusBar.setText( message );
439         if ( error )
440           fp.showingError = true;
441         else
442           fp.showingError = false;
443     }
444 
445     /// Updates the information about the selected font
fireUpdateFontInfo()446     public void fireUpdateFontInfo() {
447         if ( showFontInfoCBMI.getState() ) {
448             String infos[] = fp.getFontInfo();
449             for ( int i = 0; i < fontInfos.length; i++ )
450               fontInfos[i].setText( infos[i] );
451             fontInfoDialog.pack();
452         }
453     }
454 
setupFontList(int rangeStart, int rangeEnd)455     private void setupFontList(int rangeStart, int rangeEnd) {
456 
457         int listCount = fontMenu.getItemCount();
458         int size = 16;
459 
460         try {
461             size =  Float.valueOf(sizeField.getText()).intValue();
462         }
463         catch ( Exception e ) {
464             System.out.println("Invalid font size in the size textField. Using default value of 16");
465         }
466 
467         int style = fontStyles[styleMenu.getSelectedIndex()];
468         Font f;
469         for (int i = 0; i < listCount; i++) {
470             String fontName = (String)fontMenu.getItemAt(i);
471             f = new Font(fontName, style, size);
472             if ((rm.getSelectedIndex() != RangeMenu.SURROGATES_AREA_INDEX) &&
473                 canDisplayRange(f, rangeStart, rangeEnd)) {
474                 fontMenu.setBit(i, true);
475             }
476             else {
477                 fontMenu.setBit(i, false);
478             }
479         }
480 
481         fontMenu.repaint();
482     }
483 
canDisplayRange(Font font, int rangeStart, int rangeEnd)484     protected boolean canDisplayRange(Font font, int rangeStart, int rangeEnd) {
485         for (int i = rangeStart; i < rangeEnd; i++) {
486             if (font.canDisplay(i)) {
487                 return true;
488             }
489         }
490         return false;
491     }
492 
493     /// Displays a file load/save dialog and returns the specified file
promptFile( boolean isSave, String initFileName )494     private String promptFile( boolean isSave, String initFileName ) {
495         int retVal;
496         String str;
497 
498         /// ABP
499         if ( filePromptDialog == null)
500                 return null;
501 
502         if ( isSave ) {
503             filePromptDialog.setDialogType( JFileChooser.SAVE_DIALOG );
504             filePromptDialog.setDialogTitle( "Save..." );
505             str = "Save";
506 
507 
508         }
509         else {
510             filePromptDialog.setDialogType( JFileChooser.OPEN_DIALOG );
511             filePromptDialog.setDialogTitle( "Load..." );
512             str = "Load";
513         }
514 
515         if (initFileName != null)
516                 filePromptDialog.setSelectedFile( new File( initFileName ) );
517         retVal = filePromptDialog.showDialog( this, str );
518 
519         if ( retVal == JFileChooser.APPROVE_OPTION ) {
520                 File file = filePromptDialog.getSelectedFile();
521                 String fileName = file.getAbsolutePath();
522                 if ( fileName != null ) {
523                         return fileName;
524                 }
525         }
526 
527         return null;
528     }
529 
530     /// Converts user text into arrays of String, delimited at newline character
531     /// Also replaces any valid escape sequence with appropriate unicode character
532     /// Support \\UXXXXXX notation for surrogates
parseUserText( String orig )533     private String[] parseUserText( String orig ) {
534         int length = orig.length();
535         StringTokenizer perLine = new StringTokenizer( orig, "\n" );
536         String textLines[] = new String[ perLine.countTokens() ];
537         int lineNumber = 0;
538 
539         while ( perLine.hasMoreElements() ) {
540             StringBuffer converted = new StringBuffer();
541             String oneLine = perLine.nextToken();
542             int lineLength = oneLine.length();
543             int prevEscapeEnd = 0;
544             int nextEscape = -1;
545             do {
546                 int nextBMPEscape = oneLine.indexOf( "\\u", prevEscapeEnd );
547                 int nextSupEscape = oneLine.indexOf( "\\U", prevEscapeEnd );
548                 nextEscape = (nextBMPEscape < 0)
549                     ? ((nextSupEscape < 0)
550                        ? -1
551                        : nextSupEscape)
552                     : ((nextSupEscape < 0)
553                        ? nextBMPEscape
554                        : Math.min(nextBMPEscape, nextSupEscape));
555 
556                 if ( nextEscape != -1 ) {
557                     if ( prevEscapeEnd < nextEscape )
558                         converted.append( oneLine.substring( prevEscapeEnd, nextEscape ));
559 
560                     prevEscapeEnd = nextEscape + (nextEscape == nextBMPEscape ? 6 : 8);
561                     try {
562                         String hex = oneLine.substring( nextEscape + 2, prevEscapeEnd );
563                         if (nextEscape == nextBMPEscape) {
564                             converted.append( (char) Integer.parseInt( hex, 16 ));
565                         } else {
566                             converted.append( new String( Character.toChars( Integer.parseInt( hex, 16 ))));
567                         }
568                     }
569                     catch ( Exception e ) {
570                         int copyLimit = Math.min(lineLength, prevEscapeEnd);
571                         converted.append( oneLine.substring( nextEscape, copyLimit ));
572                     }
573                 }
574             } while (nextEscape != -1);
575             if ( prevEscapeEnd < lineLength )
576               converted.append( oneLine.substring( prevEscapeEnd, lineLength ));
577             textLines[ lineNumber++ ] = converted.toString();
578         }
579         return textLines;
580     }
581 
582     /// Reads the text from specified file, detecting UTF-16 encoding
583     /// Then breaks the text into String array, delimited at every line break
readTextFile( String fileName )584     private void readTextFile( String fileName ) {
585         try {
586             String fileText, 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
writeCurrentOptions( String fileName )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
updateGUI()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.show();
676         else
677           userTextDialog.hide();
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
loadOptions( String fileName )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.show();
796             }
797             else
798               fontInfoDialog.hide();
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
loadComparisonPNG( String fileName )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.show();
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
actionPerformed( ActionEvent e )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.show();
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.hide();
896                       fp.doPrint( i );
897                   }
898             }
899             else if ( itemName.equals( "Cancel" ))
900               printDialog.hide();
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 
stateChanged(ChangeEvent e)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
itemStateChanged( ItemEvent e )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.show();
999                 }
1000                 else
1001                   fontInfoDialog.hide();
1002             }
1003         }
1004     }
1005 
printUsage()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
main(String argv[])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.show();
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 
ImagePanel( BufferedImage image )1051         public ImagePanel( BufferedImage image ) {
1052             bi = image;
1053         }
1054 
getPreferredSize()1055         public Dimension getPreferredSize() {
1056             return new Dimension( bi.getWidth(), bi.getHeight() );
1057         }
1058 
paintComponent( Graphics g )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 {
ButtonV2( String name, ActionListener al )1066         public ButtonV2( String name, ActionListener al ) {
1067             super( name );
1068             this.addActionListener( al );
1069         }
1070     }
1071 
1072     private final class ChoiceV2 extends JComboBox {
1073 
1074         private BitSet bitSet = null;
1075 
ChoiceV2()1076         public ChoiceV2() {;}
1077 
ChoiceV2( ActionListener al )1078         public ChoiceV2( ActionListener al ) {
1079             super();
1080             this.addActionListener( al );
1081         }
1082 
ChoiceV2( ActionListener al, boolean fontChoice)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 
getToolTipText()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 
setBit(int bitIndex, boolean value)1110         public void setBit(int bitIndex, boolean value) {
1111             bitSet.set(bitIndex, value);
1112         }
1113 
getBit(int bitIndex)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 
ChoiceV2Renderer(ChoiceV2 choice)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 
getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)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 {
LabelV2( String name )1175         public LabelV2( String name ) {
1176             super( name );
1177         }
1178     }
1179 
1180     private final class MenuItemV2 extends JMenuItem {
MenuItemV2( String name, ActionListener al )1181         public MenuItemV2( String name, ActionListener al ) {
1182             super( name );
1183             this.addActionListener( al );
1184         }
1185     }
1186 
1187     private final class CheckboxMenuItemV2 extends JCheckBoxMenuItem {
CheckboxMenuItemV2( String name, boolean b, ItemListener il )1188         public CheckboxMenuItemV2( String name, boolean b, ItemListener il ) {
1189             super( name, b );
1190             this.addItemListener( il );
1191         }
1192     }
1193 }
1194