1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html#License
3/*
4 *******************************************************************************
5 * Copyright (C) 1996-2010, International Business Machines Corporation and    *
6 * others. All Rights Reserved.                                                *
7 *******************************************************************************
8 */
9package com.ibm.icu.dev.demo.rbnf;
10
11import java.awt.BorderLayout;
12import java.awt.Button;
13import java.awt.CardLayout;
14import java.awt.Checkbox;
15import java.awt.Choice;
16import java.awt.Component;
17import java.awt.Dimension;
18import java.awt.Font;
19import java.awt.FontMetrics;
20import java.awt.Frame;
21import java.awt.Graphics;
22import java.awt.GridLayout;
23import java.awt.Panel;
24import java.awt.ScrollPane;
25import java.awt.TextArea;
26import java.awt.TextComponent;
27import java.awt.TextField;
28import java.awt.event.ActionEvent;
29import java.awt.event.ActionListener;
30import java.awt.event.FocusAdapter;
31import java.awt.event.FocusEvent;
32import java.awt.event.FocusListener;
33import java.awt.event.ItemEvent;
34import java.awt.event.ItemListener;
35import java.awt.event.KeyAdapter;
36import java.awt.event.KeyEvent;
37import java.awt.event.KeyListener;
38import java.awt.event.TextEvent;
39import java.awt.event.TextListener;
40import java.awt.event.WindowAdapter;
41import java.awt.event.WindowEvent;
42import java.text.BreakIterator;
43import java.text.DecimalFormat;
44import java.text.ParsePosition;
45import java.util.Locale;
46
47import com.ibm.icu.dev.demo.impl.DemoApplet;
48import com.ibm.icu.text.RuleBasedNumberFormat;
49
50public class RbnfDemo extends DemoApplet {
51    /**
52     * For serialization
53     */
54    private static final long serialVersionUID = -9119861296873763536L;
55
56    /**
57     * Puts a copyright in the .class file
58     */
59//    private static final String copyrightNotice
60//        = "Copyright \u00a91997-1998 IBM Corp.  All rights reserved.";
61
62    /*
63     * code to run the demo as an application
64     */
65    public static void main(String[] argv) {
66        new RbnfDemo().showDemo();
67    }
68
69    protected Dimension getDefaultFrameSize(DemoApplet applet, Frame f) {
70        return new Dimension(430,270);
71    }
72
73    protected Frame createDemoFrame(DemoApplet applet) {
74        final Frame window = new Frame("Number Spellout Demo");
75        window.setSize(800, 600);
76        window.setLayout(new BorderLayout());
77
78        Panel mainPanel = new Panel();
79        mainPanel.setLayout(new GridLayout(1,2));
80
81        commentaryField = new TextArea("", 0, 0, TextArea.SCROLLBARS_VERTICAL_ONLY);
82        commentaryField.setSize(800, 50);
83        commentaryField.setText(RbnfSampleRuleSets.sampleRuleSetCommentary[0]);
84        commentaryField.setEditable(false);
85        commentaryField.setFont(new Font("Helvetica", Font.PLAIN, 14));
86
87        spelloutFormatter = new RuleBasedNumberFormat(RbnfSampleRuleSets.usEnglish, Locale.US);
88        spelloutFormatter.setLenientParseMode(lenientParse);
89        populateRuleSetMenu();
90        numberFormatter = new DecimalFormat("#,##0.##########");
91        parsePosition = new ParsePosition(0);
92        theNumber = 0;
93
94        numberField = new TextField();
95        numberField.setFont(new Font("Serif", Font.PLAIN, 24));
96        textField = new DemoTextFieldHolder();
97        textField.setFont(new Font("Serif", Font.PLAIN, 24));
98        rulesField = new DemoTextFieldHolder();
99        rulesField.setFont(new Font("Serif", Font.PLAIN, 14));
100        lenientParseButton = new Checkbox("Lenient parse", lenientParse);
101
102        numberField.addTextListener(new TextListener() {
103            public void textValueChanged(TextEvent e) {
104                if (!numberFieldHasFocus)
105                    return;
106
107                String fieldText = ((TextComponent)(e.getSource())).getText();
108                parsePosition.setIndex(0);
109                Number temp = numberFormatter.parse(fieldText, parsePosition);
110                if (temp == null || parsePosition.getIndex() == 0) {
111                    theNumber = 0;
112                    textField.setText("PARSE ERROR");
113                }
114                else {
115                    theNumber = temp.doubleValue();
116                    textField.setText(spelloutFormatter.format(theNumber, ruleSetName));
117                }
118            }
119        } );
120
121        numberField.addFocusListener(new FocusAdapter() {
122            public void focusLost(FocusEvent e) {
123                numberFieldHasFocus = false;
124                numberField.setText(numberFormatter.format(theNumber));
125            }
126
127            public void focusGained(FocusEvent e) {
128                numberFieldHasFocus = true;
129                numberField.selectAll();
130            }
131        } );
132
133        textField.addKeyListener(new KeyAdapter() {
134            public void keyTyped(KeyEvent e) {
135                if (e.getKeyChar() == '\t') {
136                    String fieldText = ((TextComponent)(e.getSource())).getText();
137                    parsePosition.setIndex(0);
138                    theNumber = spelloutFormatter.parse(fieldText, parsePosition)
139                                        .doubleValue();
140                    if (parsePosition.getIndex() == 0) {
141                        theNumber = 0;
142                        numberField.setText("PARSE ERROR");
143                        textField.selectAll();
144                    }
145                    else if (parsePosition.getIndex() < fieldText.length()) {
146                        textField.select(parsePosition.getIndex(), fieldText.length());
147                        numberField.setText(numberFormatter.format(theNumber));
148                    }
149                    else {
150                        textField.selectAll();
151                        numberField.setText(numberFormatter.format(theNumber));
152                    }
153                    e.consume();
154                }
155            }
156        } );
157
158        textField.addFocusListener(new FocusAdapter() {
159            public void focusLost(FocusEvent e) {
160                String fieldText = ((TextComponent)(e.getSource())).getText();
161                parsePosition.setIndex(0);
162                theNumber = spelloutFormatter.parse(fieldText, parsePosition)
163                                .doubleValue();
164                if (parsePosition.getIndex() == 0)
165                    numberField.setText("PARSE ERROR");
166                else
167                    numberField.setText(numberFormatter.format(theNumber));
168                textField.setText(textField.getText()); // textField.repaint() didn't work right
169            }
170
171            public void focusGained(FocusEvent e) {
172                textField.selectAll();
173            }
174        } );
175
176        rulesField.addKeyListener(new KeyAdapter() {
177            public void keyTyped(KeyEvent e) {
178                if (e.getKeyChar() == '\t') {
179                    String fieldText = ((TextComponent)(e.getSource())).getText();
180                    if (formatterMenu.getSelectedItem().equals("Custom") || !fieldText.equals(
181                                    RbnfSampleRuleSets.sampleRuleSets[formatterMenu.getSelectedIndex()])) {
182                        try {
183                            RuleBasedNumberFormat temp = new RuleBasedNumberFormat(fieldText);
184                            temp.setLenientParseMode(lenientParse);
185                            populateRuleSetMenu();
186                            spelloutFormatter = temp;
187                            customRuleSet = fieldText;
188                            formatterMenu.select("Custom");
189                            commentaryField.setText(RbnfSampleRuleSets.
190                                sampleRuleSetCommentary[RbnfSampleRuleSets.
191                                sampleRuleSetCommentary.length - 1]);
192                            redisplay();
193                        }
194                        catch (Exception x) {
195                            textField.setText(x.toString());
196                        }
197                    }
198                    e.consume();
199                }
200            }
201        } );
202
203        rulesField.addFocusListener(new FocusAdapter() {
204            public void focusLost(FocusEvent e) {
205                String fieldText = ((TextComponent)(e.getSource())).getText();
206                if (formatterMenu.getSelectedItem().equals("Custom") || !fieldText.equals(
207                                RbnfSampleRuleSets.sampleRuleSets[formatterMenu.getSelectedIndex()])) {
208                    try {
209                        RuleBasedNumberFormat temp = new RuleBasedNumberFormat(fieldText);
210                        temp.setLenientParseMode(lenientParse);
211                        populateRuleSetMenu();
212                        spelloutFormatter = temp;
213                        customRuleSet = fieldText;
214                        formatterMenu.select("Custom");
215                        redisplay();
216                    }
217                    catch (Exception x) {
218                        textField.setText(x.toString());
219                    }
220                }
221                rulesField.setText(rulesField.getText()); // rulesField.repaint() didn't work right
222            }
223        } );
224
225        lenientParseButton.addItemListener(new ItemListener() {
226            public void itemStateChanged(ItemEvent e) {
227                lenientParse = lenientParseButton.getState();
228                spelloutFormatter.setLenientParseMode(lenientParse);
229            }
230        } );
231
232        numberField.setText(numberFormatter.format(theNumber));
233        numberField.selectAll();
234        textField.setText(spelloutFormatter.format(theNumber, ruleSetName));
235
236        Panel leftPanel = new Panel();
237        leftPanel.setLayout(new BorderLayout());
238        Panel panel = new Panel();
239        panel.setLayout(new BorderLayout());
240        Panel panel1 = new Panel();
241        panel1.setLayout(new GridLayout(3, 1));
242        panel1.add(new Panel());
243        panel1.add(numberField, "Center");
244        panel1.add(lenientParseButton);
245        panel.add(panel1, "Center");
246        Panel panel2 = new Panel();
247        panel2.setLayout(new GridLayout(3, 3));
248        Button button = new Button("+100");
249        button.addActionListener( new ActionListener() {
250            public void actionPerformed(ActionEvent e) {
251                roll(100);
252            }
253        } );
254        panel2.add(button);
255        button = new Button("+10");
256        button.addActionListener( new ActionListener() {
257            public void actionPerformed(ActionEvent e) {
258                roll(10);
259            }
260        } );
261        panel2.add(button);
262        button = new Button("+1");
263        button.addActionListener( new ActionListener() {
264            public void actionPerformed(ActionEvent e) {
265                roll(1);
266            }
267        } );
268        panel2.add(button);
269        button = new Button("<");
270        button.addActionListener( new ActionListener() {
271            public void actionPerformed(ActionEvent e) {
272                theNumber *= 10;
273                redisplay();
274            }
275        } );
276        panel2.add(button);
277        panel2.add(new Panel());
278        button = new Button(">");
279        button.addActionListener( new ActionListener() {
280            public void actionPerformed(ActionEvent e) {
281                theNumber /= 10;
282                redisplay();
283            }
284        } );
285        panel2.add(button);
286        button = new Button("-100");
287        button.addActionListener( new ActionListener() {
288            public void actionPerformed(ActionEvent e) {
289                roll(-100);
290            }
291        } );
292        panel2.add(button);
293        button = new Button("-10");
294        button.addActionListener( new ActionListener() {
295            public void actionPerformed(ActionEvent e) {
296                roll(-10);
297            }
298        } );
299        panel2.add(button);
300        button = new Button("-1");
301        button.addActionListener( new ActionListener() {
302            public void actionPerformed(ActionEvent e) {
303                roll(-1);
304            }
305        } );
306        panel2.add(button);
307        panel.add(panel2, "East");
308        leftPanel.add(panel, "North");
309        leftPanel.add(textField, "Center");
310
311        Panel rightPanel = new Panel();
312        rightPanel.setLayout(new BorderLayout());
313        formatterMenu = new Choice();
314        for (int i = 0; i < RbnfSampleRuleSets.sampleRuleSetNames.length; i++)
315            formatterMenu.addItem(RbnfSampleRuleSets.sampleRuleSetNames[i]);
316        formatterMenu.addItem("Custom");
317        formatterMenu.addItemListener(new ItemListener() {
318            public void itemStateChanged(ItemEvent e) {
319                Choice source = (Choice)(e.getSource());
320                int item = source.getSelectedIndex();
321                Locale locale = RbnfSampleRuleSets.sampleRuleSetLocales[item];
322
323                commentaryField.setText(RbnfSampleRuleSets.
324                                sampleRuleSetCommentary[item]);
325
326                if (locale != null && (locale.getLanguage().equals("iw")
327                        || locale.getLanguage().equals("ru") || locale.getLanguage().equals("ja")
328                        || locale.getLanguage().equals("el")
329                        || locale.getLanguage().equals("zh"))) {
330                    textField.togglePanes(false);
331                    rulesField.togglePanes(false);
332                }
333                else {
334                    textField.togglePanes(true);
335                    rulesField.togglePanes(true);
336                }
337
338                makeNewSpelloutFormatter();
339                redisplay();
340            }
341        } );
342
343        ruleSetMenu = new Choice();
344        populateRuleSetMenu();
345
346        ruleSetMenu.addItemListener(new ItemListener() {
347            public void itemStateChanged(ItemEvent e) {
348                ruleSetName = ruleSetMenu.getSelectedItem();
349                redisplay();
350            }
351        } );
352
353        Panel menuPanel = new Panel();
354        menuPanel.setLayout(new GridLayout(1, 2));
355        menuPanel.add(formatterMenu);
356        menuPanel.add(ruleSetMenu);
357        rightPanel.add(menuPanel, "North");
358
359        rulesField.setText(RbnfSampleRuleSets.sampleRuleSets[formatterMenu.getSelectedIndex()]);
360        rightPanel.add(rulesField, "Center");
361
362        mainPanel.add(leftPanel);
363        mainPanel.add(rightPanel);
364
365        window.add(mainPanel, "Center");
366        window.add(commentaryField, "South");
367
368        window.doLayout();
369        window.show();
370        final DemoApplet theApplet = applet;
371        window.addWindowListener(
372                new WindowAdapter() {
373                    public void windowClosing(WindowEvent e) {
374                        setVisible(false);
375                        window.dispose();
376
377                        if (theApplet != null) {
378                            theApplet.demoClosed();
379                        } else System.exit(0);
380                    }
381                } );
382        return window;
383    }
384
385    void roll(int delta) {
386        theNumber += delta;
387        redisplay();
388    }
389
390    void redisplay() {
391        numberField.setText(numberFormatter.format(theNumber));
392        textField.setText(spelloutFormatter.format(theNumber, ruleSetName));
393    }
394
395    void makeNewSpelloutFormatter() {
396        int item = formatterMenu.getSelectedIndex();
397        String formatterMenuItem = formatterMenu.getSelectedItem();
398
399        if (formatterMenuItem.equals("Custom")) {
400            rulesField.setText(customRuleSet);
401            spelloutFormatter = new RuleBasedNumberFormat(customRuleSet);
402        }
403        else {
404            rulesField.setText(RbnfSampleRuleSets.sampleRuleSets[item]);
405
406            Locale locale = RbnfSampleRuleSets.sampleRuleSetLocales[item];
407            if (locale == null)
408                locale = Locale.getDefault();
409
410            spelloutFormatter = new RuleBasedNumberFormat(RbnfSampleRuleSets.
411                            sampleRuleSets[item], locale);
412        }
413        spelloutFormatter.setLenientParseMode(lenientParse);
414        populateRuleSetMenu();
415    }
416
417    void populateRuleSetMenu() {
418        String[] ruleSetNames = spelloutFormatter.getRuleSetNames();
419
420        if (ruleSetMenu != null) {
421            ruleSetMenu.removeAll();
422            for (int i = 0; i < ruleSetNames.length; i++)
423                ruleSetMenu.addItem(ruleSetNames[i]);
424
425            ruleSetName = ruleSetMenu.getSelectedItem();
426        }
427        else
428            ruleSetName = ruleSetNames[0];
429    }
430
431//    private Frame demoWindow = null;
432
433    private TextComponent numberField;
434    private DemoTextFieldHolder textField;
435    private DemoTextFieldHolder rulesField;
436    private TextComponent commentaryField;
437    private Checkbox lenientParseButton;
438
439    private boolean numberFieldHasFocus = true;
440
441    private RuleBasedNumberFormat spelloutFormatter;
442    private DecimalFormat numberFormatter;
443    private ParsePosition parsePosition;
444
445    private boolean lenientParse = true;
446
447    private double theNumber = 0;
448//    private boolean canEdit = true;
449
450    private Choice formatterMenu;
451    private Choice ruleSetMenu;
452    private String ruleSetName;
453
454    private String customRuleSet = "NO RULES!";
455}
456
457class DemoTextField extends Component {
458    /**
459     * For serialization
460     */
461    private static final long serialVersionUID = -7947090021239472658L;
462    public DemoTextField() {
463    }
464
465    public void setText(String text) {
466        this.text = text;
467        this.repaint();
468    }
469
470    public String getText() {
471        return text;
472    }
473
474    public void paint(Graphics g) {
475        Font font = getFont();
476        FontMetrics fm = g.getFontMetrics();
477        g.setFont(font);
478        String txt = getText();
479        BreakIterator bi = BreakIterator.getLineInstance();
480        bi.setText(txt);
481        int lineHeight = fm.getHeight();
482        int width = getSize().width;
483        int penY = fm.getAscent();
484        int lineStart = 0;
485        int tempLineEnd = bi.first();
486        int lineEnd = 0;
487        int maxLineEnd = 0;
488        totalHeight = 0;
489
490        while (lineStart < txt.length()) {
491            maxLineEnd = txt.indexOf('\n', lineStart);
492            if (maxLineEnd == -1)
493                maxLineEnd = Integer.MAX_VALUE;
494            while (tempLineEnd != BreakIterator.DONE && fm.stringWidth(txt.substring(
495                            lineStart, tempLineEnd)) < width) {
496                lineEnd = tempLineEnd;
497                tempLineEnd = bi.next();
498            }
499            if (lineStart >= lineEnd) {
500                if (tempLineEnd == BreakIterator.DONE)
501                    lineEnd = txt.length();
502                else
503                    lineEnd = tempLineEnd;
504            }
505            if (lineEnd > maxLineEnd)
506                lineEnd = maxLineEnd;
507            g.drawString(txt.substring(lineStart, lineEnd), 0, penY);
508            penY += lineHeight;
509            totalHeight += lineHeight;
510            lineStart = lineEnd;
511            if (lineStart < txt.length() && txt.charAt(lineStart) == '\n')
512                ++lineStart;
513        }
514    }
515
516/*
517    public Dimension getPreferredSize() {
518        Dimension size = getParent().getSize();
519        return new Dimension(size.width, totalHeight);
520    }
521*/
522
523    private String text;
524    private int totalHeight;
525}
526
527class DemoTextFieldHolder extends Panel {
528    /**
529     * For serialization
530     */
531    private static final long serialVersionUID = 7514498764062569858L;
532    public DemoTextFieldHolder() {
533        tf1 = new TextArea("", 0, 0, TextArea.SCROLLBARS_VERTICAL_ONLY);
534        tf2 = new DemoTextField();
535        sp = new ScrollPane();
536
537        setLayout(new CardLayout());
538
539        sp.add(tf2, "TextField1");
540        sp.setVisible(false);
541        add(tf1, "TestField2");
542        add(sp, "ScrollPane");
543    }
544
545    public void addFocusListener(FocusListener l) {
546        tf1.addFocusListener(l);
547    }
548
549    public void addKeyListener(KeyListener l) {
550        tf1.addKeyListener(l);
551    }
552
553    public void setText(String text) {
554        tf1.setText(text);
555        tf2.setText(text);
556    }
557
558    public String getText() {
559        return tf1.getText();
560    }
561
562    public void select(int start, int end) {
563        tf1.select(start, end);
564    }
565
566    public void selectAll() {
567        tf1.selectAll();
568    }
569
570    public void togglePanes(boolean canShowRealTextField) {
571        if (canShowRealTextField != showingRealTextField) {
572            CardLayout layout = (CardLayout)(getLayout());
573            layout.next(this);
574            showingRealTextField = canShowRealTextField;
575        }
576    }
577
578    private TextArea tf1 = null;
579    private DemoTextField tf2 = null;
580    private ScrollPane sp = null;
581    private boolean showingRealTextField = true;
582}
583