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
44import java.awt.BorderLayout;
45import java.awt.Font;
46import java.awt.event.ActionEvent;
47import java.awt.event.ActionListener;
48import java.awt.event.ItemEvent;
49import java.awt.event.ItemListener;
50
51import javax.swing.*;
52
53import java.util.*;
54import 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
65public 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}
259