2 *******************************************************************************
\r
3 * Copyright (C) 1996-2010, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 *******************************************************************************
\r
7 package com.ibm.icu.dev.demo.impl;
\r
8 import java.awt.AWTEventMulticaster;
\r
9 import java.awt.Canvas;
\r
10 import java.awt.Color;
\r
11 import java.awt.Cursor;
\r
12 import java.awt.Dimension;
\r
13 import java.awt.Font;
\r
14 import java.awt.FontMetrics;
\r
15 import java.awt.Graphics;
\r
16 import java.awt.Image;
\r
17 import java.awt.Point;
\r
18 import java.awt.datatransfer.Clipboard;
\r
19 import java.awt.datatransfer.DataFlavor;
\r
20 import java.awt.datatransfer.StringSelection;
\r
21 import java.awt.datatransfer.Transferable;
\r
22 import java.awt.event.ActionEvent;
\r
23 import java.awt.event.ActionListener;
\r
24 import java.awt.event.FocusEvent;
\r
25 import java.awt.event.FocusListener;
\r
26 import java.awt.event.InputEvent;
\r
27 import java.awt.event.KeyEvent;
\r
28 import java.awt.event.KeyListener;
\r
29 import java.awt.event.MouseEvent;
\r
30 import java.awt.event.MouseListener;
\r
31 import java.awt.event.MouseMotionListener;
\r
32 import java.awt.event.TextEvent;
\r
33 import java.awt.event.TextListener;
\r
34 import java.text.BreakIterator;
\r
36 // LIU: Changed from final to non-final
\r
37 public class DumbTextComponent extends Canvas
\r
38 implements KeyListener, MouseListener, MouseMotionListener, FocusListener
\r
44 private static final long serialVersionUID = 8265547730738652151L;
\r
46 // private transient static final String copyright =
\r
47 // "Copyright \u00A9 1998, Mark Davis. All Rights Reserved.";
\r
48 private transient static boolean DEBUG = false;
\r
50 private String contents = "";
\r
51 private Selection selection = new Selection();
\r
52 private int activeStart = -1;
\r
53 private boolean editable = true;
\r
55 private transient Selection tempSelection = new Selection();
\r
56 private transient boolean focus;
\r
57 private transient BreakIterator lineBreaker = BreakIterator.getLineInstance();
\r
58 private transient BreakIterator wordBreaker = BreakIterator.getWordInstance();
\r
59 private transient BreakIterator charBreaker = BreakIterator.getCharacterInstance();
\r
60 private transient int lineAscent;
\r
61 private transient int lineHeight;
\r
62 private transient int lineLeading;
\r
63 private transient int lastHeight = 10;
\r
64 private transient int lastWidth = 50;
\r
65 private static final int MAX_LINES = 200; // LIU: Use symbolic name
\r
66 private transient int[] lineStarts = new int[MAX_LINES]; // LIU
\r
67 private transient int lineCount = 1;
\r
69 private transient boolean valid = false;
\r
70 private transient FontMetrics fm;
\r
71 private transient boolean redoLines = true;
\r
72 private transient boolean doubleClick = false;
\r
73 private transient TextListener textListener;
\r
74 private transient ActionListener selectionListener;
\r
75 private transient Image cacheImage;
\r
76 private transient Dimension mySize;
\r
77 private transient int xInset = 5;
\r
78 private transient int yInset = 5;
\r
79 private transient Point startPoint = new Point();
\r
80 private transient Point endPoint = new Point();
\r
81 private transient Point caretPoint = new Point();
\r
82 private transient Point activePoint = new Point();
\r
84 //private transient static String clipBoard;
\r
86 private static final char CR = '\015'; // LIU
\r
88 // ============================================
\r
90 public DumbTextComponent() {
\r
91 addMouseListener(this);
\r
92 addMouseMotionListener(this);
\r
93 addKeyListener(this);
\r
94 addFocusListener(this);
\r
95 setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
\r
99 // ================ Events ====================
\r
101 // public boolean isFocusTraversable() { return true; }
\r
103 public void addActionListener(ActionListener l) {
\r
104 selectionListener = AWTEventMulticaster.add(selectionListener, l);
\r
107 public void removeActionListener(ActionListener l) {
\r
108 selectionListener = AWTEventMulticaster.remove(selectionListener, l);
\r
111 public void addTextListener(TextListener l) {
\r
112 textListener = AWTEventMulticaster.add(textListener, l);
\r
115 public void removeTextListener(TextListener l) {
\r
116 textListener = AWTEventMulticaster.remove(textListener, l);
\r
119 private transient boolean pressed;
\r
121 public void mousePressed(MouseEvent e) {
\r
122 if (DEBUG) System.out.println("mousePressed");
\r
126 doubleClick = e.getClickCount() > 1;
\r
133 public void mouseDragged(MouseEvent e) {
\r
134 if (DEBUG) System.out.println("mouseDragged");
\r
138 public void mouseReleased(MouseEvent e) {
\r
139 if (DEBUG) System.out.println("mouseReleased");
\r
143 public void mouseEntered(MouseEvent e) {
\r
144 //if (pressed) select(e, false);
\r
147 public void mouseExited(MouseEvent e){
\r
148 //if (pressed) select(e, false);
\r
151 public void mouseClicked(MouseEvent e) {}
\r
152 public void mouseMoved(MouseEvent e) {}
\r
155 public void focusGained(FocusEvent e) {
\r
156 if (DEBUG) System.out.println("focusGained");
\r
161 public void focusLost(FocusEvent e) {
\r
162 if (DEBUG) System.out.println("focusLost");
\r
168 public void select(MouseEvent e, boolean first) {
\r
170 point2Offset(e.getPoint(), tempSelection);
\r
172 if ((e.getModifiers() & InputEvent.SHIFT_MASK) == 0) {
\r
173 tempSelection.anchor = tempSelection.caret;
\r
178 tempSelection.expand(wordBreaker);
\r
180 select(tempSelection);
\r
183 public void keyPressed(KeyEvent e) {
\r
184 int code = e.getKeyCode();
\r
185 if (DEBUG) System.out.println("keyPressed "
\r
186 + hex((char)code) + ", " + hex((char)e.getModifiers()));
\r
187 int start = selection.getStart();
\r
188 int end = selection.getEnd();
\r
189 boolean shift = (e.getModifiers() & InputEvent.SHIFT_MASK) != 0;
\r
190 boolean ctrl = (e.getModifiers() & InputEvent.CTRL_MASK) != 0;
\r
193 case KeyEvent.VK_Q:
\r
194 if (!ctrl || !editable) break;
\r
198 case KeyEvent.VK_V:
\r
201 this.getToolkit().beep();
\r
206 case KeyEvent.VK_C:
\r
210 case KeyEvent.VK_X:
\r
213 this.getToolkit().beep();
\r
219 case KeyEvent.VK_A:
\r
222 select(Integer.MAX_VALUE, 0, false);
\r
224 case KeyEvent.VK_RIGHT:
\r
226 tempSelection.set(selection);
\r
227 tempSelection.nextBound(ctrl ? wordBreaker : charBreaker, +1, shift);
\r
228 select(tempSelection);
\r
230 case KeyEvent.VK_LEFT:
\r
232 tempSelection.set(selection);
\r
233 tempSelection.nextBound(ctrl ? wordBreaker : charBreaker, -1, shift);
\r
234 select(tempSelection);
\r
236 case KeyEvent.VK_UP: // LIU: Add support for up arrow
\r
238 tempSelection.set(selection);
\r
239 tempSelection.caret = lineDelta(tempSelection.caret, -1);
\r
241 tempSelection.anchor = tempSelection.caret;
\r
243 select(tempSelection);
\r
245 case KeyEvent.VK_DOWN: // LIU: Add support for down arrow
\r
247 tempSelection.set(selection);
\r
248 tempSelection.caret = lineDelta(tempSelection.caret, +1);
\r
250 tempSelection.anchor = tempSelection.caret;
\r
252 select(tempSelection);
\r
254 case KeyEvent.VK_DELETE: // LIU: Add delete key support
\r
255 if (!editable) break;
\r
257 if (contents.length() == 0) break;
\r
258 start = selection.getStart();
\r
259 end = selection.getEnd();
\r
260 if (start == end) {
\r
262 if (end > contents.length()) {
\r
263 getToolkit().beep();
\r
267 replaceRange("", start, end);
\r
273 Clipboard cb = this.getToolkit().getSystemClipboard();
\r
274 StringSelection ss = new StringSelection(
\r
275 contents.substring(selection.getStart(), selection.getEnd()));
\r
276 cb.setContents(ss, ss);
\r
280 Clipboard cb = this.getToolkit().getSystemClipboard();
\r
281 Transferable t = cb.getContents(this);
\r
283 this.getToolkit().beep();
\r
287 String temp = (String) t.getTransferData(DataFlavor.stringFlavor);
\r
289 } catch (Exception e) {
\r
290 this.getToolkit().beep();
\r
295 * LIU: Given an offset into contents, moves up or down by lines,
\r
296 * according to lineStarts[].
\r
297 * @param off the offset into contents
\r
298 * @param delta how many lines to move up (< 0) or down (> 0)
\r
299 * @return the new offset into contents
\r
301 private int lineDelta(int off, int delta) {
\r
302 int line = findLine(off, false);
\r
303 int posInLine = off - lineStarts[line];
\r
304 // System.out.println("off=" + off + " at " + line + ":" + posInLine);
\r
307 line = posInLine = 0;
\r
308 } else if (line >= lineCount) {
\r
309 return contents.length();
\r
311 off = lineStarts[line] + posInLine;
\r
312 if (off >= lineStarts[line+1]) {
\r
313 off = lineStarts[line+1] - 1;
\r
318 public void keyReleased(KeyEvent e) {
\r
319 int code = e.getKeyCode();
\r
320 if (DEBUG) System.out.println("keyReleased "
\r
321 + hex((char)code) + ", " + hex((char)e.getModifiers()));
\r
324 public void keyTyped(KeyEvent e) {
\r
325 char ch = e.getKeyChar();
\r
326 if (DEBUG) System.out.println("keyTyped "
\r
327 + hex((char)ch) + ", " + hex((char)e.getModifiers()));
\r
328 if ((e.getModifiers() & InputEvent.CTRL_MASK) != 0) return;
\r
331 case KeyEvent.CHAR_UNDEFINED:
\r
333 case KeyEvent.VK_BACK_SPACE:
\r
335 if (!editable) break;
\r
336 if (contents.length() == 0) break;
\r
337 start = selection.getStart();
\r
338 end = selection.getEnd();
\r
339 if (start == end) {
\r
342 getToolkit().beep(); // LIU: Add audio feedback of NOP
\r
346 replaceRange("", start, end);
\r
348 case KeyEvent.VK_DELETE:
\r
350 if (!editable) break;
\r
351 if (contents.length() == 0) break;
\r
352 start = selection.getStart();
\r
353 end = selection.getEnd();
\r
354 if (start == end) {
\r
356 if (end > contents.length()) {
\r
357 getToolkit().beep(); // LIU: Add audio feedback of NOP
\r
361 replaceRange("", start, end);
\r
364 if (!editable) break;
\r
365 // LIU: Dispatch to subclass API
\r
371 // LIU: Subclass API for handling of key typing
\r
372 protected void handleKeyTyped(KeyEvent e) {
\r
373 insertText(String.valueOf(e.getKeyChar()));
\r
376 protected void setKeyStart(int keyStart) {
\r
377 if (activeStart != keyStart) {
\r
378 activeStart = keyStart;
\r
383 protected void validateKeyStart() {
\r
384 if (activeStart > selection.getStart()) {
\r
385 activeStart = selection.getStart();
\r
390 protected int getKeyStart() {
\r
391 return activeStart;
\r
394 // ===================== Control ======================
\r
396 public synchronized void setEditable(boolean b) {
\r
400 public boolean isEditable() {
\r
404 public void select(Selection newSelection) {
\r
405 newSelection.pin(contents);
\r
406 if (!selection.equals(newSelection)) {
\r
407 selection.set(newSelection);
\r
408 if (selectionListener != null) {
\r
409 selectionListener.actionPerformed(
\r
410 new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
\r
411 "Selection Changed", 0));
\r
418 public void select(int start, int end) {
\r
419 select(start, end, false);
\r
422 public void select(int start, int end, boolean clickAfter) {
\r
423 tempSelection.set(start, end, clickAfter);
\r
424 select(tempSelection);
\r
427 public int getSelectionStart() {
\r
428 return selection.getStart();
\r
431 public int getSelectionEnd() {
\r
432 return selection.getEnd();
\r
435 public void setBounds(int x, int y, int w, int h) {
\r
436 super.setBounds(x,y,w,h);
\r
440 public Dimension getPreferredSize() {
\r
441 return new Dimension(lastWidth,lastHeight);
\r
444 public Dimension getMaximumSize() {
\r
445 return new Dimension(lastWidth,lastHeight);
\r
448 public Dimension getMinimumSize() {
\r
449 return new Dimension(lastHeight,lastHeight);
\r
452 public void setText(String text) {
\r
454 select(tempSelection.set(selection).pin(contents));
\r
457 public void setText2(String text) {
\r
459 charBreaker.setText(text);
\r
460 wordBreaker.setText(text);
\r
461 lineBreaker.setText(text);
\r
463 if (textListener != null)
\r
464 textListener.textValueChanged(
\r
465 new TextEvent(this, TextEvent.TEXT_VALUE_CHANGED));
\r
469 public void insertText(String text) {
\r
470 if (activeStart == -1) activeStart = selection.getStart();
\r
471 replaceRange(text, selection.getStart(), selection.getEnd());
\r
474 public void replaceRange(String s, int start, int end) {
\r
475 setText2(contents.substring(0,start) + s
\r
476 + contents.substring(end));
\r
477 select(tempSelection.set(selection).
\r
478 fixAfterReplace(start, end, s.length()));
\r
479 validateKeyStart();
\r
482 public String getText() {
\r
486 public void setFont(Font font) {
\r
487 super.setFont(font);
\r
492 // ================== Graphics ======================
\r
494 public void update(Graphics g) {
\r
495 if (DEBUG) System.out.println("update");
\r
499 public void paint(Graphics g) {
\r
500 mySize = getSize();
\r
501 if (cacheImage == null
\r
502 || cacheImage.getHeight(this) != mySize.height
\r
503 || cacheImage.getWidth(this) != mySize.width) {
\r
504 cacheImage = createImage(mySize.width, mySize.height);
\r
507 if (!valid || redoLines) {
\r
508 if (DEBUG) System.out.println("painting");
\r
509 paint2(cacheImage.getGraphics());
\r
512 //getToolkit().sync();
\r
513 if (DEBUG) System.out.println("copying");
\r
514 g.drawImage(cacheImage,
\r
515 0, 0, mySize.width, mySize.height,
\r
516 0, 0, mySize.width, mySize.height,
\r
520 public void paint2(Graphics g) {
\r
521 g.clearRect(0, 0, mySize.width, mySize.height);
\r
522 if (DEBUG) System.out.println("print");
\r
523 if (focus) g.setColor(Color.black);
\r
524 else g.setColor(Color.gray);
\r
525 g.drawRect(0,0,mySize.width-1,mySize.height-1);
\r
527 mySize.width-2,mySize.height-2);
\r
528 g.setColor(Color.black);
\r
529 g.setFont(getFont());
\r
530 fm = g.getFontMetrics();
\r
531 lineAscent = fm.getAscent();
\r
532 lineLeading = fm.getLeading();
\r
533 lineHeight = lineAscent + fm.getDescent() + lineLeading;
\r
534 int y = yInset + lineAscent;
\r
535 String lastSubstring = "";
\r
536 if (redoLines) fixLineStarts(mySize.width-xInset-xInset);
\r
537 for (int i = 0; i < lineCount; y += lineHeight, ++i) {
\r
538 // LIU: Don't display terminating ^M characters
\r
539 int lim = lineStarts[i+1];
\r
540 if (lim > 0 && contents.length() > 0 &&
\r
541 contents.charAt(lim-1) == CR) --lim;
\r
542 lastSubstring = contents.substring(lineStarts[i],lim);
\r
543 g.drawString(lastSubstring, xInset, y);
\r
545 drawSelection(g, lastSubstring);
\r
546 lastHeight = y + yInset - lineHeight + yInset;
\r
547 lastWidth = mySize.width-xInset-xInset;
\r
550 void paintRect(Graphics g, int x, int y, int w, int h) {
\r
552 g.fillRect(x, y, w, h);
\r
554 g.drawRect(x, y, w-1, h-1);
\r
558 public void drawSelection(Graphics g, String lastSubstring) {
\r
559 g.setXORMode(Color.black);
\r
560 if (activeStart != -1) {
\r
561 offset2Point(activeStart, false, activePoint);
\r
562 g.setColor(Color.magenta);
\r
563 int line = activePoint.x - 1;
\r
564 g.fillRect(line, activePoint.y, 1, lineHeight);
\r
566 if (selection.isCaret()) {
\r
567 offset2Point(selection.caret, selection.clickAfter, caretPoint);
\r
569 if (focus) g.setColor(Color.blue);
\r
570 else g.setColor(Color.yellow);
\r
571 offset2Point(selection.getStart(), true, startPoint);
\r
572 offset2Point(selection.getEnd(), false, endPoint);
\r
573 if (selection.getStart() == selection.caret)
\r
574 caretPoint.setLocation(startPoint);
\r
575 else caretPoint.setLocation(endPoint);
\r
576 if (startPoint.y == endPoint.y) {
\r
577 paintRect(g, startPoint.x, startPoint.y,
\r
578 Math.max(1,endPoint.x-startPoint.x), lineHeight);
\r
580 paintRect(g, startPoint.x, startPoint.y,
\r
581 (mySize.width-xInset)-startPoint.x, lineHeight);
\r
582 if (startPoint.y + lineHeight < endPoint.y)
\r
583 paintRect(g, xInset, startPoint.y + lineHeight,
\r
584 (mySize.width-xInset)-xInset, endPoint.y - startPoint.y - lineHeight);
\r
585 paintRect(g, xInset, endPoint.y, endPoint.x-xInset, lineHeight);
\r
588 if (focus || selection.isCaret()) {
\r
589 if (focus) g.setColor(Color.green);
\r
590 else g.setColor(Color.red);
\r
591 int line = caretPoint.x - (selection.clickAfter ? 0 : 1);
\r
592 g.fillRect(line, caretPoint.y, 1, lineHeight);
\r
593 int w = lineHeight/12 + 1;
\r
594 int braces = line - (selection.clickAfter ? -1 : w);
\r
595 g.fillRect(braces, caretPoint.y, w, 1);
\r
596 g.fillRect(braces, caretPoint.y + lineHeight - 1, w, 1);
\r
600 public Point offset2Point(int off, boolean start, Point p) {
\r
601 int line = findLine(off, start);
\r
604 width = fm.stringWidth(
\r
605 contents.substring(lineStarts[line], off));
\r
606 } catch (Exception e) {
\r
607 System.out.println(e);
\r
609 p.x = width + xInset;
\r
610 if (p.x > mySize.width - xInset)
\r
611 p.x = mySize.width - xInset;
\r
612 p.y = lineHeight * line + yInset;
\r
616 private int findLine(int off, boolean start) {
\r
617 // if it is start, then go to the next line!
\r
619 for (int i = 1; i < lineCount; ++i) {
\r
620 // LIU: This was <= ; changed to < to make caret after
\r
621 // final CR in line appear at START of next line.
\r
622 if (off < lineStarts[i]) return i-1;
\r
624 // LIU: Check for special case; after CR at end of the last line
\r
625 if (off == lineStarts[lineCount] &&
\r
626 off > 0 && contents.length() > 0 && contents.charAt(off-1) == CR) {
\r
629 return lineCount-1;
\r
632 // offsets on any line will go from start,true to end,false
\r
633 // excluding start,false and end,true
\r
634 public Selection point2Offset(Point p, Selection o) {
\r
635 if (p.y < yInset) {
\r
637 o.clickAfter = true;
\r
640 int line = (p.y - yInset)/lineHeight;
\r
641 if (line >= lineCount) {
\r
642 o.caret = contents.length();
\r
643 o.clickAfter = false;
\r
646 int target = p.x - xInset;
\r
648 o.caret = lineStarts[line];
\r
649 o.clickAfter = true;
\r
652 int lowGuess = lineStarts[line];
\r
654 int highGuess = lineStarts[line+1];
\r
655 int highWidth = fm.stringWidth(contents.substring(lineStarts[line],highGuess));
\r
656 if (target >= highWidth) {
\r
657 o.caret = lineStarts[line+1];
\r
658 o.clickAfter = false;
\r
661 while (lowGuess < highGuess - 1) {
\r
662 int guess = (lowGuess + highGuess)/2;
\r
663 int width = fm.stringWidth(contents.substring(lineStarts[line],guess));
\r
664 if (width <= target) {
\r
667 if (width == target) break;
\r
673 // at end, either lowWidth < target < width(low+1), or lowWidth = target
\r
674 int highBound = charBreaker.following(lowGuess);
\r
675 int lowBound = charBreaker.previous();
\r
676 // we are now at character boundaries
\r
677 if (lowBound != lowGuess)
\r
678 lowWidth = fm.stringWidth(contents.substring(lineStarts[line],lowBound));
\r
679 if (highBound != highGuess)
\r
680 highWidth = fm.stringWidth(contents.substring(lineStarts[line],highBound));
\r
681 // we now have the right widths
\r
682 if (target - lowWidth < highWidth - target) {
\r
683 o.caret = lowBound;
\r
684 o.clickAfter = true;
\r
686 o.caret = highBound;
\r
687 o.clickAfter = false;
\r
689 // we now have the closest!
\r
693 private void fixLineStarts(int width) {
\r
696 if (contents.length() == 0) {
\r
701 // LIU: Add check for MAX_LINES
\r
702 for (int start = 0; start < contents.length() && lineCount < MAX_LINES;
\r
704 end = nextLine(fm, start, width);
\r
705 lineStarts[lineCount++] = end;
\r
706 if (end == start) { // LIU: Assertion
\r
707 throw new RuntimeException("nextLine broken");
\r
714 // LIU: Enhanced to wrap long lines. Bug with return of start fixed.
\r
715 public int nextLine(FontMetrics fMtr, int start, int width) {
\r
716 int len = contents.length();
\r
717 for (int i = start; i < len; ++i) {
\r
718 // check for line separator
\r
719 char ch = (contents.charAt(i));
\r
720 if (ch >= 0x000A && ch <= 0x000D || ch == 0x2028 || ch == 0x2029) {
\r
722 if (ch == 0x000D && i+1 < len && contents.charAt(i+1) == 0x000A) // crlf
\r
723 ++len; // grab extra char
\r
727 String subject = contents.substring(start,len);
\r
728 if (visibleWidth(fMtr, subject) <= width)
\r
731 // LIU: Remainder of this method rewritten to accomodate lines
\r
732 // longer than the component width by first trying to break
\r
733 // into lines; then words; finally chars.
\r
734 int n = findFittingBreak(fMtr, subject, width, lineBreaker);
\r
736 n = findFittingBreak(fMtr, subject, width, wordBreaker);
\r
739 n = findFittingBreak(fMtr, subject, width, charBreaker);
\r
741 return n > 0 ? start + n : len;
\r
745 * LIU: Finds the longest substring that fits a given width
\r
746 * composed of subunits returned by a BreakIterator. If the smallest
\r
747 * subunit is too long, returns 0.
\r
748 * @param fMtr metrics to use
\r
749 * @param line the string to be fix into width
\r
750 * @param width line.substring(0, result) must be <= width
\r
751 * @param breaker the BreakIterator that will be used to find subunits
\r
752 * @return maximum characters, at boundaries returned by breaker,
\r
753 * that fit into width, or zero on failure
\r
755 private int findFittingBreak(FontMetrics fMtr, String line, int width,
\r
756 BreakIterator breaker) {
\r
757 breaker.setText(line);
\r
758 int last = breaker.first();
\r
759 int end = breaker.next();
\r
760 while (end != BreakIterator.DONE &&
\r
761 visibleWidth(fMtr, line.substring(0, end)) <= width) {
\r
763 end = breaker.next();
\r
768 public int visibleWidth(FontMetrics fMtr, String s) {
\r
770 for (i = s.length()-1; i >= 0; --i) {
\r
771 char ch = s.charAt(i);
\r
772 if (!(ch == ' ' || ch >= 0x000A && ch <= 0x000D || ch == 0x2028 || ch == 0x2029))
\r
773 return fMtr.stringWidth(s.substring(0,i+1));
\r
778 // =============== Utility ====================
\r
780 private void fixHex() {
\r
781 if (selection.getEnd() == 0) return;
\r
785 int min = Math.min(8,selection.getEnd());
\r
786 for (int i = 0; i < min; ++i) {
\r
787 char ch = contents.charAt(selection.getEnd()-1-i);
\r
788 int value = Character.getNumericValue(ch);
\r
789 if (value < 0 || value > 15) break;
\r
790 store += places * value;
\r
795 int bottom = store & 0xFFFF;
\r
796 if (store >= 0xD8000000 && store < 0xDC000000
\r
797 && bottom >= 0xDC00 && bottom < 0xE000) { // surrogates
\r
798 add = "" + (char)(store >> 16) + (char)bottom;
\r
799 } else if (store > 0xFFFF && store <= 0x10FFFF) {
\r
801 add = "" + (char)(((store >> 10) & 0x3FF) + 0xD800)
\r
802 + (char)((store & 0x3FF) + 0xDC00);
\r
804 } else if (count >= 4) {
\r
806 add = ""+(char)(store & 0xFFFF);
\r
809 char ch = contents.charAt(selection.getEnd()-1);
\r
811 if (ch >= 0xDC00 && ch <= 0xDFFF && selection.getEnd() > 1) {
\r
812 ch = contents.charAt(selection.getEnd()-2);
\r
813 if (ch >= 0xD800 && ch <= 0xDBFF) {
\r
815 add = hex(ch) + add;
\r
819 replaceRange(add, selection.getEnd()-count, selection.getEnd());
\r
822 public static String hex(char ch) {
\r
823 String result = Integer.toString(ch,16).toUpperCase();
\r
824 result = "0000".substring(result.length(),4) + result;
\r