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