1 /*
   2  * Copyright (c) 2000, 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  */
  43 
  44 import java.awt.BorderLayout;
  45 import java.awt.Font;
  46 import java.awt.event.ActionEvent;
  47 import java.awt.event.ActionListener;
  48 import java.awt.event.ItemEvent;
  49 import java.awt.event.ItemListener;
  50 
  51 import javax.swing.*;
  52 
  53 import java.util.*;
  54 import java.util.regex.*;
  55 
  56 /**
  57  * RangeMenu.java
  58  *
  59  * @author Shinsuke Fukuda
  60  * @author Ankit Patel [Conversion to Swing - 01/07/30]
  61  */
  62 
  63 /// Custom made choice menu that holds data for unicode range
  64 
  65 public final class RangeMenu extends JComboBox implements ActionListener {
  66 
  67     private static final int[][] UNICODE_RANGES = getUnicodeRanges();
  68     private static final String[] UNICODE_RANGE_NAMES = getUnicodeRangeNames();
  69 
  70     private boolean useCustomRange = false;
  71     private int[] customRange = { 0x0000, 0x007f };
  72 
  73     /// Custom range dialog variables
  74     private final JDialog customRangeDialog;
  75     private final JTextField customRangeStart = new JTextField( "0000", 4 );
  76     private final JTextField customRangeEnd   = new JTextField( "007F", 4 );
  77     private final int CUSTOM_RANGE_INDEX = UNICODE_RANGE_NAMES.length - 1;
  78 
  79     /// Parent Font2DTest Object holder
  80     private final Font2DTest parent;
  81 
  82     public static final int SURROGATES_AREA_INDEX = 91;
  83 
  84     public RangeMenu( Font2DTest demo, JFrame f ) {
  85         super();
  86         parent = demo;
  87 
  88         for ( int i = 0; i < UNICODE_RANGE_NAMES.length; i++ )
  89           addItem( UNICODE_RANGE_NAMES[i] );
  90 
  91         setSelectedIndex( 0 );
  92         addActionListener( this );
  93 
  94         /// Set up custom range dialog...
  95         customRangeDialog = new JDialog( f, "Custom Unicode Range", true );
  96         customRangeDialog.setResizable( false );
  97 
  98         JPanel dialogTop = new JPanel();
  99         JPanel dialogBottom = new JPanel();
 100         JButton okButton = new JButton("OK");
 101         JLabel from = new JLabel( "From:" );
 102         JLabel to = new JLabel("To:");
 103         Font labelFont = new Font( "dialog", Font.BOLD, 12 );
 104         from.setFont( labelFont );
 105         to.setFont( labelFont );
 106         okButton.setFont( labelFont );
 107 
 108         dialogTop.add( from );
 109         dialogTop.add( customRangeStart );
 110         dialogTop.add( to );
 111         dialogTop.add( customRangeEnd );
 112         dialogBottom.add( okButton );
 113         okButton.addActionListener( this );
 114 
 115         customRangeDialog.getContentPane().setLayout( new BorderLayout() );
 116         customRangeDialog.getContentPane().add( "North", dialogTop );
 117         customRangeDialog.getContentPane().add( "South", dialogBottom );
 118         customRangeDialog.pack();
 119     }
 120 
 121     /// Return the range that is currently selected
 122 
 123     public int[] getSelectedRange() {
 124         if ( useCustomRange ) {
 125             int startIndex, endIndex;
 126             String startText, endText;
 127             String empty = "";
 128             try {
 129                 startText = customRangeStart.getText().trim();
 130                 endText = customRangeEnd.getText().trim();
 131                 if ( startText.equals(empty) && !endText.equals(empty) ) {
 132                     endIndex = Integer.parseInt( endText, 16 );
 133                     startIndex = endIndex - 7*25;
 134                 }
 135                 else if ( !startText.equals(empty) && endText.equals(empty) ) {
 136                     startIndex = Integer.parseInt( startText, 16 );
 137                     endIndex = startIndex + 7*25;
 138                 }
 139                 else {
 140                     startIndex = Integer.parseInt( customRangeStart.getText(), 16 );
 141                     endIndex = Integer.parseInt( customRangeEnd.getText(), 16 );
 142                 }
 143             }
 144             catch ( Exception e ) {
 145                 /// Error in parsing the hex number ---
 146                 /// Reset the range to what it was before and return that
 147                 customRangeStart.setText( Integer.toString( customRange[0], 16 ));
 148                 customRangeEnd.setText( Integer.toString( customRange[1], 16 ));
 149                 return customRange;
 150             }
 151 
 152             if ( startIndex < 0 )
 153               startIndex = 0;
 154             if ( endIndex > 0xffff )
 155               endIndex = 0xffff;
 156             if ( startIndex > endIndex )
 157               startIndex = endIndex;
 158 
 159             customRange[0] = startIndex;
 160             customRange[1] = endIndex;
 161             return customRange;
 162         }
 163         else
 164           return UNICODE_RANGES[ getSelectedIndex() ];
 165     }
 166 
 167     /// Function used by loadOptions in Font2DTest main panel
 168     /// to reset setting and range selection
 169     public void setSelectedRange( String name, int start, int end ) {
 170         setSelectedItem( name );
 171         customRange[0] = start;
 172         customRange[1] = end;
 173         parent.fireRangeChanged();
 174     }
 175 
 176     /// ActionListener interface function
 177     /// ABP
 178     /// moved JComboBox event code into this fcn from
 179     /// itemStateChanged() method. Part of change to Swing.
 180     public void actionPerformed( ActionEvent e ) {
 181         Object source = e.getSource();
 182 
 183         if ( source instanceof JComboBox ) {
 184                 String rangeName = (String)((JComboBox)source).getSelectedItem();
 185 
 186                 if ( rangeName.equals("Custom...") ) {
 187                     useCustomRange = true;
 188                     customRangeDialog.setLocationRelativeTo(parent);
 189                     customRangeDialog.show();
 190                 }
 191                 else {
 192                   useCustomRange = false;
 193                 }
 194                 parent.fireRangeChanged();
 195         }
 196         else if ( source instanceof JButton ) {
 197                 /// Since it is only "OK" button that sends any action here...
 198                 customRangeDialog.hide();
 199         }
 200     }
 201 
 202     private static int[][] getUnicodeRanges() {
 203         List<Integer> ranges = new ArrayList<>();
 204         ranges.add(0);
 205         Character.UnicodeBlock currentBlock = Character.UnicodeBlock.of(0);
 206         for (int cp = 0x000001; cp < 0x110000; cp++ ) {
 207             Character.UnicodeBlock ub = Character.UnicodeBlock.of(cp);
 208             if (currentBlock == null) {
 209                 if (ub != null) {
 210                     ranges.add(cp);
 211                     currentBlock = ub;
 212                 }
 213             } else {  // being in some unicode range
 214                 if (ub == null) {
 215                     ranges.add(cp - 1);
 216                     currentBlock = null;
 217                 } else if (cp == 0x10ffff) {  // end of last block
 218                     ranges.add(cp);
 219                 } else if (! ub.equals(currentBlock)) {
 220                     ranges.add(cp - 1);
 221                     ranges.add(cp);
 222                     currentBlock = ub;
 223                 }
 224             }
 225         }
 226         ranges.add(0x00);  // for user defined range.
 227         ranges.add(0x7f);  // for user defined range.
 228 
 229         int[][] returnval = new int[ranges.size() / 2][2];
 230         for (int i = 0 ; i < ranges.size() / 2 ; i++ ) {
 231             returnval[i][0] = ranges.get(2*i);
 232             returnval[i][1] = ranges.get(2*i + 1);
 233         }
 234         return returnval;
 235     }
 236 
 237     private static String[] getUnicodeRangeNames() {
 238         String[] names = new String[UNICODE_RANGES.length];
 239         for (int i = 0 ; i < names.length ; i++ ) {
 240             names[i] = titleCase(
 241                 Character.UnicodeBlock.of(UNICODE_RANGES[i][0]).toString());
 242         }
 243         names[names.length - 1] = "Custom...";
 244         return names;
 245     }
 246 
 247     private static String titleCase(String str) {
 248         str = str.replaceAll("_", " ");
 249         Pattern p = Pattern.compile("(^|\\W)([a-z])");
 250         Matcher m = p.matcher(str.toLowerCase(Locale.ROOT));
 251         StringBuffer sb = new StringBuffer();
 252         while (m.find()) {
 253             m.appendReplacement(sb, m.group(1) + m.group(2).toUpperCase(Locale.ROOT));
 254         }
 255         m.appendTail(sb);
 256         return sb.toString().replace("Cjk", "CJK").replace("Nko", "NKo");
 257     }
 258 }