source: trunk/src/org/expeditee/items/Text.java@ 105

Last change on this file since 105 was 105, checked in by ra33, 16 years ago

Made a whole lot of changes...

Including using polyLine etc for drawing rectangles nicer

File size: 42.4 KB
Line 
1package org.expeditee.items;
2
3import java.awt.BasicStroke;
4import java.awt.Color;
5import java.awt.Dimension;
6import java.awt.Font;
7import java.awt.Graphics2D;
8import java.awt.Point;
9import java.awt.Polygon;
10import java.awt.Rectangle;
11import java.awt.Stroke;
12import java.awt.event.KeyEvent;
13import java.awt.event.MouseEvent;
14import java.awt.font.FontRenderContext;
15import java.awt.font.LineBreakMeasurer;
16import java.awt.font.TextAttribute;
17import java.awt.font.TextHitInfo;
18import java.awt.font.TextLayout;
19import java.awt.geom.AffineTransform;
20import java.awt.geom.Rectangle2D;
21import java.text.AttributedString;
22import java.util.LinkedList;
23import java.util.List;
24
25import org.expeditee.gui.DisplayIO;
26import org.expeditee.gui.FrameGraphics;
27import org.expeditee.gui.FrameKeyboardActions;
28import org.expeditee.gui.FrameMouseActions;
29
30/**
31 * Represents text displayed on the screen, which may include multiple lines.
32 * All standard text properties can be set (font, style, size).
33 *
34 * @author jdm18
35 *
36 */
37public class Text extends Item {
38
39 private String[] _processedText = null;
40
41 public String[] getProcessedText() {
42 return _processedText;
43 }
44
45 public void setProcessedText(String[] tokens) {
46 _processedText = tokens;
47 }
48
49 public static final String FRAME_NAME_SEPARATOR = " on frame ";
50
51 /**
52 * The default font used to display text items if no font is specified.
53 */
54 public static final String DEFAULT_FONT = "Serif-Plain-18";
55
56 public static final Color DEFAULT_COLOR = Color.BLACK;
57
58 public static final int MINIMUM_RANGED_CHARS = 2;
59
60 public static final int NONE = 0;
61
62 public static final int UP = 1;
63
64 public static final int DOWN = 2;
65
66 public static final int LEFT = 3;
67
68 public static final int RIGHT = 4;
69
70 public static final int HOME = 5;
71
72 public static final int END = 6;
73
74 public static final int PAGE_DOWN = 7;
75
76 public static final int PAGE_UP = 8;
77
78 private int _maxWidth = -1;
79
80 private Justification _justification = Justification.none;
81
82 private int _spacing = -1;
83
84 private int _word_spacing = -1;
85
86 private int _initial_spacing = -1;
87
88 private int _letter_spacing = -1;
89
90 // used during ranging out
91 private int _selectionStart = -1;
92
93 private int _selectionEnd = -1;
94
95 // text is broken up into lines
96 private StringBuffer _text = new StringBuffer();
97
98 private List<TextLayout> _textLayouts = new LinkedList<TextLayout>();
99
100 private List<Integer> _lineOffsets = new LinkedList<Integer>();
101
102 private LineBreakMeasurer _lineBreaker = null;
103
104 // The font to display this text in
105 private Font _font;
106
107 // the rectangle surrounding this text item
108 private Polygon _poly = null;
109
110 /**
111 * Creates a new Text Item with the given ID and text.
112 *
113 * @param id
114 * The id of this item
115 * @param text
116 * The text to use in this item
117 */
118 public Text(int id, String text) {
119 super();
120 _text.append(text);
121 rebuild(false);
122
123 setID(id);
124 }
125
126 /**
127 * Creates a new Text Item with the given ID
128 *
129 * @param id
130 * The ID to of this item
131 */
132 public Text(int id) {
133 super();
134 setID(id);
135 }
136
137 public Text(int i, String string, Color foreground, Color background) {
138 this(i, string);
139 this.setColor(foreground);
140 this.setBackgroundColor(background);
141 }
142
143 /**
144 * Sets the maximum width of this Text item when justifcation is used.
145 * passing in 0 or -1 means there is no maximum width
146 *
147 * @param width
148 * The maximum width of this item when justification is applied
149 * to it.
150 */
151 @Override
152 public void setWidth(int width) {
153 // 0 is the default
154 if (width == 0)
155 width = -1;
156
157 _maxWidth = width;
158 }
159
160 /**
161 * Returns the maximum width of this Text item when justifcation is used.
162 * Note the returned value may be -1, which indicates that there is no
163 * maximum size set
164 *
165 * @return The maximum width of this Text item when justification is used
166 */
167 @Override
168 public int getWidth() {
169 return _maxWidth;
170 }
171
172 /**
173 * Sets the justification of this Text item. The given integer should
174 * correspond to one of the JUSTIFICATION constants defined in Item
175 *
176 * @param just
177 * The justification to apply to this Text item
178 */
179 public void setJustification(Justification just) {
180 _justification = just;
181 rejustify();
182 }
183
184 /**
185 * Returns the current justification of this Text item, the default value is
186 * Item.JUSTIFICATION_NONE
187 *
188 * @return The justification of this Text item
189 */
190 public Justification getJustification() {
191 if (_justification == null || _justification.equals(Justification.none))
192 return null;
193 return _justification;
194 }
195
196 private int getJustOffset(TextLayout layout) {
197 if (getWidth() < 0)
198 return 0;
199
200 if (getJustification() == Justification.center)
201 return (int) ((getWidth() - layout.getAdvance()) / 2);
202 else if (getJustification() == Justification.right)
203 return (int) (getWidth() - layout.getAdvance());
204
205 return 0;
206 }
207
208 /**
209 * Sets the text displayed on the screen to the given String
210 *
211 * @param text
212 * The String to display on the screen when drawing this Item.
213 */
214 public void setText(String text) {
215 _text = new StringBuffer(text);
216 rebuild(false);
217 }
218
219 public void setTextList(List<String> text) {
220 if (text == null || text.size() <= 0)
221 return;
222
223 setText(text.get(0));
224 for (int i = 1; i < text.size(); i++) {
225 appendText("\n");
226 appendText(text.get(i));
227 }
228 rebuild(false);
229 }
230
231 public void setAttributeValue(String value) {
232
233 }
234
235 /**
236 * Inserts the given String at the start of the first line of this Text
237 * Item.
238 *
239 * @param text
240 * The String to insert.
241 */
242 public void prependText(String text) {
243 _text.insert(0, text);
244 rebuild(false);
245 }
246
247 /**
248 * If the first line of text starts with the given String, then it is
249 * removed otherwise no action is taken.
250 *
251 * @param text
252 * The String to remove from the first line of Text
253 */
254 public void removeText(String text) {
255 if (_text.length() > 0 && _text.indexOf(text) == 0)
256 _text.delete(0, text.length());
257
258 }
259
260 public void removeEndText(String textToRemove) {
261 int length = _text.length();
262 if (length > 0) {
263 int pos = _text.indexOf(textToRemove);
264 int textToRemoveLength = textToRemove.length();
265 if (pos + textToRemoveLength == length) {
266 _text.delete(pos, length);
267 }
268 }
269 }
270
271 /**
272 * Appends the given String to any text already present in this Item
273 *
274 * @param text
275 * The String to append.
276 */
277 public void appendText(String text) {
278 _text.append(text);
279 rebuild(false);
280 }
281
282 public void appendLine(String text) {
283 if (text == null)
284 text = "";
285
286 if (_text.length() > 0)
287 _text.append('\n');
288
289 _text.append(text);
290 rebuild(false);
291 }
292
293 /**
294 * Tests if the first line of this Text starts with the given String.
295 *
296 * @param text
297 * The prefix to check for
298 * @return True if the first line starts with the given String, False
299 * otherwise.
300 */
301 public boolean startsWith(String text) {
302 return startsWith(text, true);
303 }
304
305 public boolean startsWith(String text, boolean ignoreCase) {
306 if (text == null || _text == null || _text.length() < 1)
307 return false;
308
309 if (ignoreCase)
310 return _text.toString().toLowerCase()
311 .startsWith(text.toLowerCase());
312 else
313 return _text.indexOf(text) == 0;
314 }
315
316 /**
317 * Inserts a character into the Text of this Item.
318 *
319 * @param ch
320 * The character insert.
321 * @param mouseX
322 * The X position to insert the Strings at.
323 * @param mouseY
324 * The Y position to insert the Strings at.
325 */
326 public Point insertChar(char ch, int mouseX, int mouseY) {
327 if (ch != '\t')
328 return insertText("" + ch, mouseX, mouseY);
329
330 return insertText(" " + ch, mouseX, mouseY);
331 }
332
333 /**
334 * @param index
335 * @return
336 */
337 private char getNextBullet(char bullet) {
338 switch (bullet) {
339 case '*':
340 return '>';
341 case '>':
342 return '+';
343 case '+':
344 return 'o';
345 case 'o':
346 return '*';
347 }
348 return bullet;
349 }
350
351 private char getPreviousBullet(char bullet) {
352 switch (bullet) {
353 case '*':
354 return 'o';
355 case '>':
356 return '*';
357 case '+':
358 return '>';
359 case 'o':
360 return '+';
361 }
362 return bullet;
363 }
364
365 public Point getEndPosition(int mouseY) {
366 return getEdgePosition(getLinePosition(mouseY), false);
367 }
368
369 public Point getStartPosition(int mouseY) {
370 return getEdgePosition(getLinePosition(mouseY), true);
371 }
372
373 public Point getEndParagraphPosition() {
374 return getEdgePosition(_textLayouts.size() - 1, false);
375 }
376
377 public Point getStartParagraphPosition() {
378 return getEdgePosition(0, true);
379 }
380
381 private Point getEdgePosition(int line, boolean start) {
382 // if there is no text yet, or the line is invalid
383 if (_text == null || _text.length() == 0 || line < 0
384 || line > _textLayouts.size() - 1)
385 return new Point(getX(), getY());
386
387 TextLayout last = _textLayouts.get(line);
388 TextHitInfo hit;
389 if (start)
390 hit = last.getNextLeftHit(1);
391 else
392 hit = last.getNextRightHit(last.getCharacterCount() - 1);
393
394 // move the cursor to the new location
395 float[] caret = last.getCaretInfo(hit);
396 float y = getLineDrop(last) * line;
397
398 int x = ((int) (getX() + caret[0] + 0.5) + getJustOffset(last));
399 x = Math
400 .min(
401 x,
402 (getX() - Item.MARGIN_RIGHT - (2 * getGravity()) + getBoundsWidth()));
403 return new Point(x, (int) (getY() + y + caret[1] + 0.5));
404 }
405
406 public void setSelectionStart(int mouseX, int mouseY) {
407 // determine what line is being pointed to
408 int line = getLinePosition(mouseY);
409
410 // get the character being pointed to
411 TextHitInfo hit = getCharPosition(line, mouseX);
412 _selectionStart = hit.getInsertionIndex() + _lineOffsets.get(line);
413 }
414
415 public void setSelectionEnd(int mouseX, int mouseY) {
416 // determine what line is being pointed to
417 int line = getLinePosition(mouseY);
418
419 // get the character being pointed to
420 TextHitInfo hit = getCharPosition(line, mouseX);
421 _selectionEnd = hit.getInsertionIndex() + _lineOffsets.get(line);
422 }
423
424 public void clearSelection() {
425 _selectionStart = -1;
426 _selectionEnd = -1;
427 }
428
429 public void clearSelectionEnd() {
430 _selectionEnd = -1;
431 }
432
433 public String copySelectedText() {
434 if (_selectionStart < 0 || _selectionEnd < 0)
435 return null;
436 else if (_selectionEnd > _text.length())
437 _selectionEnd = _text.length();
438
439 return _text.substring(Math.min(_selectionStart, _selectionEnd), Math
440 .max(_selectionStart, _selectionEnd));
441 }
442
443 public String cutSelectedText() {
444 return replaceSelectedText("");
445 }
446
447 public String replaceSelectedText(String newText) {
448 if (_selectionStart < 0 || _selectionEnd < 0)
449 return null;
450 else if (_selectionEnd > _text.length())
451 _selectionEnd = _text.length();
452
453 int left = Math.min(_selectionStart, _selectionEnd);
454 int right = Math.max(_selectionStart, _selectionEnd);
455
456 String s = _text.substring(left, right);
457
458 _text.delete(left, right);
459 _text.insert(left, newText);
460 rebuild(true);
461
462 return s;
463 }
464
465 public int getSelectedSize() {
466 if (_selectionEnd < 0 || _selectionStart < 0)
467 return 0;
468
469 // System.out.println(_selectionStart + ":" + _selectionEnd);
470
471 return Math.abs(_selectionEnd - _selectionStart);
472 }
473
474 /**
475 * Inserts the given String into the Text at the position given by the
476 * mouseX and mouseY coordinates
477 *
478 * @param text
479 * The String to insert into this Text.
480 * @param mouseX
481 * The X position to insert the String
482 * @param mouseY
483 * The Y position to insert the String
484 * @return The new location that the mouse cursor should be moved to
485 */
486 public java.awt.Point insertText(String text, int mouseX, int mouseY) {
487 TextHitInfo hit;
488 TextLayout current = null;
489 int line;
490
491 // check for empty string
492 if (text == null || text.length() == 0)
493 return new Point(mouseX, mouseY);
494
495 // if there is no text yet
496 if (_text == null || _text.length() == 0) {
497 _text = new StringBuffer().append(text);
498 // create the linebreaker and layouts
499 rebuild(true);
500 assert (_textLayouts.size() == 1);
501 current = _textLayouts.get(0);
502 hit = current.getNextRightHit(0);
503 line = 0;
504
505 // otherwise, we are inserting text
506 } else {
507 _processedText = null;
508 // determine what line is being pointed to
509 line = getLinePosition(mouseY);
510
511 // get the character being pointed to
512 hit = getCharPosition(line, mouseX);
513
514 int pos = hit.getInsertionIndex() + _lineOffsets.get(line);
515
516 if (line > 0 && hit.getInsertionIndex() == 0)
517 pos++;
518
519 // if this is a backspace key
520 if (text.charAt(0) == KeyEvent.VK_BACK_SPACE) {
521 if (pos > 0) {
522 deleteChar(pos - 1, false);
523
524 pos--;
525 }
526 // if this is a delete key
527 } else if (text.charAt(0) == KeyEvent.VK_DELETE) {
528 if (pos < _text.length()) {
529 deleteChar(pos, false);
530 }
531 // this is a tab
532 } else if (text.charAt(0) == KeyEvent.VK_TAB) {
533 // Text length greater than 1 signals a backwards tab
534 if (text.length() > 1) {
535 // Find the first non space char to see if its a bullet
536 int index = 0;
537 for (index = 0; index < _text.length(); index++) {
538 if (!Character.isSpaceChar(_text.charAt(index)))
539 break;
540 }
541 // Check if there is a space after the bullet
542 if (index < _text.length() - 1
543 && _text.charAt(index + 1) == ' ') {
544 // Change the bullet
545 _text.setCharAt(index, getPreviousBullet(_text
546 .charAt(index)));
547 }
548 // Remove the spacing at the start
549 for (int i = 0; i < TAB_STRING.length(); i++) {
550 if (_text.length() > 0
551 && Character.isSpaceChar(_text.charAt(0))) {
552 deleteChar(0, false);
553 pos--;
554 } else
555 break;
556 }
557 _lineBreaker = null;
558 } else {
559 // / Find the first non space char to see if its a bullet
560 int index = 0;
561 for (index = 0; index < _text.length(); index++) {
562 if (!Character.isSpaceChar(_text.charAt(index)))
563 break;
564 }
565 // Check if there is a space after the bullet
566 if (index < _text.length() - 1
567 && _text.charAt(index + 1) == ' ') {
568 char nextBullet = getNextBullet(_text.charAt(index));
569 // Change the bullet
570 _text.setCharAt(index, nextBullet);
571 }
572 // Insert the spacing at the start
573 insertString(TAB_STRING, 0);
574 pos += TAB_STRING.length();
575 }
576 // this is a normal insert
577 } else {
578 insertString(text, pos);
579 pos += text.length();
580 }
581
582 int newLine = line;
583
584 // if a rebuild is required
585 if (_lineBreaker == null)
586 rebuild(true);
587 else
588 rejustify();
589
590 // determine the new position the cursor should have
591 for (int i = 1; i < _lineOffsets.size(); i++) {
592 if (_lineOffsets.get(i) >= pos) {
593 newLine = i - 1;
594 break;
595 }
596 }
597
598 current = _textLayouts.get(newLine);
599 pos -= _lineOffsets.get(newLine);
600
601 if (newLine == line) {
602 if (pos > 0)
603 hit = current.getNextRightHit(pos - 1);
604 else
605 hit = current.getNextLeftHit(1);
606 } else if (newLine < line) {
607 hit = current.getNextRightHit(pos - 1);
608 } else {
609 hit = current.getNextRightHit(pos - 1);
610 }
611
612 line = newLine;
613 }
614
615 // move the cursor to the new location
616 float[] caret = current.getCaretInfo(hit);
617 float y = getLineDrop(current) * line;
618
619 int x = (int) (getX() + caret[0] + 0.5) + getJustOffset(current);
620 x = Math
621 .min(
622 x,
623 (getX() - Item.MARGIN_RIGHT - (2 * getGravity()) + getBoundsWidth()));
624 return new Point(x, (int) (getY() + y + caret[1] + 0.5));
625 }
626
627 public Point moveCursor(int direction, int mouseX, int mouseY) {
628 // check for home or end keys
629 if (direction == HOME)
630 return getStartParagraphPosition();
631
632 if (direction == END)
633 return getEndParagraphPosition();
634
635 TextHitInfo hit;
636 TextLayout current;
637 int line;
638
639 // if there is no text yet
640 if (_text == null || _text.length() == 0) {
641 return new Point(mouseX, mouseY);
642 // otherwise, move the cursor
643 } else {
644 // determine the line of text to check
645 line = getLinePosition(mouseY);
646 if (line < 0)
647 line = _textLayouts.size() - 1;
648
649 // if the cursor is moving up or down, change the line
650 if (direction == UP)
651 line = Math.max(line - 1, 0);
652 else if (direction == DOWN)
653 line = Math.min(line + 1, _textLayouts.size() - 1);
654
655 hit = getCharPosition(line, mouseX);
656
657 if (direction == LEFT) {
658 if (hit.getInsertionIndex() > 0) {
659 hit = _textLayouts.get(line).getNextLeftHit(hit);
660 // This takes care of the case where the user has put a hard
661 // line break in
662 if (line > 0
663 && _text.charAt(hit.getInsertionIndex()
664 + _lineOffsets.get(line)) == '\n') {
665 line--;
666 hit = _textLayouts.get(line).getNextRightHit(
667 _textLayouts.get(line).getCharacterCount() - 1);
668 }
669 // This takes care of soft line breaks.
670 } else if (line > 0) {
671 line--;
672 hit = _textLayouts.get(line).getNextRightHit(
673 _textLayouts.get(line).getCharacterCount() - 1);
674 while (hit.getCharIndex() > 0
675 && _text.charAt(_lineOffsets.get(line)
676 + hit.getCharIndex() - 1) == ' ') {
677 hit = _textLayouts.get(line).getNextLeftHit(hit);
678 }
679 }
680 } else if (direction == RIGHT) {
681 if (hit.getInsertionIndex() < _textLayouts.get(line)
682 .getCharacterCount()) {
683 hit = _textLayouts.get(line).getNextRightHit(hit);
684 while (hit.getCharIndex() > 0
685 && hit.getCharIndex() < _textLayouts.get(line)
686 .getCharacterCount()
687 && _text.charAt(_lineOffsets.get(line)
688 + hit.getCharIndex() - 1) == '\t')
689 hit = _textLayouts.get(line).getNextRightHit(hit);
690 } else if (line < _textLayouts.size() - 1) {
691 line++;
692 hit = _textLayouts.get(line).getNextLeftHit(1);
693 }
694 }
695
696 current = _textLayouts.get(line);
697 }
698
699 // move the cursor to the new location
700 float[] caret = current.getCaretInfo(hit);
701 float y = getLineDrop(current) * line;
702
703 return new Point((int) (getX() + caret[0] + 0.5)
704 + getJustOffset(current), (int) (getY() + y + caret[1] + 0.5));
705 }
706
707 /**
708 * Iterates through the given line string and returns the position of the
709 * character being pointed at by the mouse.
710 *
711 * @param line
712 * The index of the _text array of the String to be searched.
713 * @param mouseX
714 * The X coordinate of the mouse
715 * @return The position in the string of the character being pointed at.
716 */
717 private TextHitInfo getCharPosition(int line, int mouseX) {
718 if (line < 0 || line >= _textLayouts.size())
719 return null;
720
721 TextLayout layout = _textLayouts.get(line);
722 mouseX += getOffset().x;
723 mouseX -= getJustOffset(layout);
724
725 return layout.hitTestChar(mouseX - getX(), 0);
726 }
727
728 private int getLinePosition(int mouseY) {
729 mouseY += getOffset().y;
730
731 int y = getY();
732
733 for (TextLayout text : _textLayouts) {
734 // calculate X to ensure it is in the shape
735 Rectangle bounds = text.getLogicalHighlightShape(0,
736 text.getCharacterCount()).getBounds();
737
738 if (bounds.getWidth() < 1)
739 bounds = new Rectangle((int) bounds.getMinX(), (int) bounds
740 .getMinY(), 10, (int) bounds.getHeight());
741
742 double x = bounds.getCenterX();
743
744 // if(text.getLogicalHighlightShape(0,
745 // text.getCharacterCount()).contains(text.getBounds().getMinX()
746 // + 10, mouseY - getY() - (y - getY())))
747 if (bounds.contains(x, mouseY - getY() - (y - getY())))
748 return _textLayouts.indexOf(text);
749
750 // check if the cursor is between lines
751 if (mouseY - getY() - (y - getY()) < bounds.getMinY())
752 return Math.max(0, _textLayouts.indexOf(text) - 1);
753
754 y += getLineDrop(text);
755 }
756
757 return _textLayouts.size() - 1;
758 }
759
760 /**
761 * Sets the Font that this text will be displayed with on the screen.
762 *
763 * @param font
764 * The Font to display the Text of this Item in.
765 */
766 public void setFont(Font font) {
767 // all decoding occurs in the Utils class
768 _font = font;
769
770 // rejustify();
771 rebuild(false);
772 }
773
774 /**
775 * Returns the Font that this Text is currently using when painting to the
776 * screen
777 *
778 * @return The Font used to display this Text on the screen.
779 */
780 public Font getFont() {
781 return _font;
782 }
783
784 public Font getPaintFont() {
785 if (getFont() == null)
786 return Font.decode(DEFAULT_FONT);
787
788 return getFont();
789 }
790
791 public String getFamily() {
792 return getPaintFont().getFamily();
793 }
794
795 public void setFamily(String newFamily) {
796 String toDecode = newFamily + "-" + getFontStyle() + "-" + getSize();
797 setFont(Font.decode(toDecode));
798 }
799
800 public String getFontStyle() {
801 Font f = getPaintFont();
802 String s = "";
803
804 if (f.isPlain())
805 s += "Plain";
806
807 if (f.isBold())
808 s += "Bold";
809
810 if (f.isItalic())
811 s += "Italic";
812
813 return s;
814 }
815
816 public static final String MONOSPACED_FONT = "monospaced";
817
818 public static final String[] FONT_WHEEL = { "sansserif", "monospaced",
819 "serif", "dialog", "dialoginput" };
820
821 public static final char[] FONT_CHARS = { 's', 'm', 't', 'd', 'i' };
822
823 private static final int NEARBY_GRAVITY = 2;
824
825 private static final int MINIMUM_FONT_SIZE = 8;
826
827 public void toggleFontFamily() {
828 String fontFamily = getFamily().toLowerCase();
829 // set it to the first font by default
830 setFamily(FONT_WHEEL[0]);
831
832 for (int i = 0; i < FONT_WHEEL.length - 3; i++) {
833 if (fontFamily.equals(FONT_WHEEL[i])) {
834 setFamily(FONT_WHEEL[i + 1]);
835 break;
836 }
837 }
838 }
839
840 public void toggleFontStyle() {
841 Font currentFont = getPaintFont();
842 if (currentFont.isPlain())
843 setFont(currentFont.deriveFont(Font.BOLD));
844 else if (currentFont.isBold() && currentFont.isItalic())
845 setFont(currentFont.deriveFont(Font.PLAIN));
846 else if (currentFont.isBold())
847 setFont(currentFont.deriveFont(Font.ITALIC));
848 else
849 setFont(currentFont.deriveFont(Font.ITALIC + Font.BOLD));
850 }
851
852 public void setFontStyle(String newFace) {
853 if (newFace == null || newFace.trim().length() == 0) {
854 setFont(getPaintFont().deriveFont(Font.PLAIN));
855 return;
856 }
857
858 newFace = newFace.toLowerCase().trim();
859
860 if (newFace.equals("plain")) {
861 setFont(getPaintFont().deriveFont(Font.PLAIN));
862 } else if (newFace.equals("bold")) {
863 setFont(getPaintFont().deriveFont(Font.BOLD));
864 } else if (newFace.equals("italic")) {
865 setFont(getPaintFont().deriveFont(Font.ITALIC));
866 } else if (newFace.contains("bold") && newFace.contains("italic")) {
867 setFont(getPaintFont().deriveFont(Font.BOLD + Font.ITALIC));
868 }
869
870 }
871
872 /**
873 * Returns a String array of this Text object's text, split up into separate
874 * lines.
875 *
876 * @return The String array with one element per line of text in this Item.
877 */
878 public List<String> getTextList() {
879 if (_text == null)
880 return null;
881 try {
882 List<String> list = new LinkedList<String>();
883
884 int last = 0;
885 for (int offset : _lineOffsets) {
886 if (offset != last) {
887 list
888 .add(_text.substring(last, offset).replaceAll("\n",
889 ""));
890 }
891 last = offset;
892 }
893
894 return list;
895 } catch (Exception e) {
896 System.out.println(e.getMessage());
897 return null;
898 }
899 }
900
901 public String getText() {
902 return _text.toString();
903 }
904
905 /**
906 * Returns the first line of text in this Text Item
907 *
908 * @return The first line of Text
909 */
910 public String getFirstLine() {
911 if (_text == null || _text.length() == 0)
912 return null;
913
914 if (_text.indexOf("\n") < 0)
915 return _text.toString();
916
917 return _text.substring(0, _text.indexOf("\n"));
918 }
919
920 /**
921 * Sets the inter-line spacing (in pixels) of this text.
922 *
923 * @param spacing
924 * The number of pixels to allow between each line
925 */
926 public void setSpacing(int spacing) {
927 _spacing = spacing;
928 updatePolygon();
929 }
930
931 /**
932 * Returns the inter-line spacing (in pixels) of this Text.
933 *
934 * @return The spacing (inter-line) in pixels of this Text.
935 */
936 public int getSpacing() {
937 return _spacing;
938 }
939
940 private float getLineDrop(TextLayout layout) {
941 if (getSpacing() < 0)
942 return layout.getAscent() + layout.getDescent()
943 + layout.getLeading();
944
945 return layout.getAscent() + layout.getDescent() + getSpacing();
946 }
947
948 public void setWordSpacing(int spacing) {
949 _word_spacing = spacing;
950 }
951
952 public int getWordSpacing() {
953 return _word_spacing;
954 }
955
956 public void setLetterSpacing(int spacing) {
957 _letter_spacing = spacing;
958 }
959
960 public int getLetterSpacing() {
961 return _letter_spacing;
962 }
963
964 public void setInitialSpacing(int spacing) {
965 _initial_spacing = spacing;
966 }
967
968 public int getInitialSpacing() {
969 return _initial_spacing;
970 }
971
972 @Override
973 public boolean intersects(Polygon p) {
974 if (super.intersects(p)) {
975 int textY = getY();
976
977 for (TextLayout text : _textLayouts) {
978 // check left and right of each box
979 Rectangle textOutline = text.getLogicalHighlightShape(0,
980 text.getCharacterCount()).getBounds();
981 textOutline.width += 2;
982 textOutline.height += 2;
983
984 textOutline.translate(getX() - 1, textY - 1);
985 if (p.intersects(textOutline))
986 return true;
987 textY += getLineDrop(text);
988 }
989 }
990 return false;
991 }
992
993 @Override
994 public boolean contains(int mouseX, int mouseY) {
995 return contains(mouseX, mouseY, getGravity() * NEARBY_GRAVITY);
996 }
997
998 public boolean contains(int mouseX, int mouseY, int gravity) {
999 mouseX += getOffset().x;
1000 mouseY += getOffset().y;
1001
1002 int textY = getY();
1003 int textX = getX();
1004
1005 Rectangle outline = getPolygon().getBounds();
1006
1007 // Check if its outside the top and left and bottom bounds
1008 if (outline.x - mouseX > gravity || outline.y - mouseY > gravity
1009 || mouseY - (outline.y + outline.height) > gravity
1010 || mouseX - (outline.x + outline.width) > gravity) {
1011 return false;
1012 }
1013
1014 for (TextLayout text : _textLayouts) {
1015 // check left and right of each box
1016 Rectangle textOutline = text.getLogicalHighlightShape(0,
1017 text.getCharacterCount()).getBounds();
1018
1019 // check if the cursor is within the top, bottom and within the
1020 // gravity of right
1021 if (mouseY - textY > textOutline.y
1022 && mouseY - textY < textOutline.y + textOutline.height
1023 && mouseX - textX < textOutline.width + gravity
1024 + Item.MARGIN_RIGHT)
1025 return true;
1026 textY += getLineDrop(text);
1027 }
1028
1029 return false;
1030 }
1031
1032 /**
1033 * Updates the Polygon (rectangle) that surrounds this Text on the screen.
1034 */
1035 protected void updatePolygon() {
1036 // if there is no text, there is nothing to do
1037 if (_text == null)
1038 return;
1039
1040 _poly = new Polygon();
1041
1042 if (_textLayouts.size() < 1)
1043 return;
1044
1045 int minX = Integer.MAX_VALUE;
1046 int maxX = Integer.MIN_VALUE;
1047
1048 int minY = Integer.MAX_VALUE;
1049 int maxY = Integer.MIN_VALUE;
1050
1051 float y = -1;
1052
1053 synchronized (_textLayouts) {
1054 for (TextLayout layout : _textLayouts) {
1055 Rectangle2D bounds = layout.getLogicalHighlightShape(0,
1056 layout.getCharacterCount()).getBounds2D();
1057
1058 if (y < 0)
1059 y = 0;
1060 else
1061 y += getLineDrop(layout);
1062
1063 maxX = Math.max(maxX, (int) bounds.getMaxX());
1064 minX = Math.min(minX, (int) bounds.getMinX());
1065 maxY = Math.max(maxY, (int) (bounds.getMaxY() + y));
1066 minY = Math.min(minY, (int) (bounds.getMinY() + y));
1067 }
1068 }
1069
1070 minX -= getLeftMargin();
1071 maxX += Item.MARGIN_RIGHT;
1072
1073 _poly.addPoint(minX - getGravity(), minY - getGravity());
1074 _poly.addPoint(maxX + getGravity(), minY - getGravity());
1075 _poly.addPoint(maxX + getGravity(), maxY + getGravity());
1076 _poly.addPoint(minX - getGravity(), maxY + getGravity());
1077 }
1078
1079 // TODO it seems like this method has some exponencial processing which
1080 // makes items copy really slowly when there are lots of lines of text!
1081 // This needs to be fixed!!
1082 private void rebuild(boolean limitWidth) {
1083 // if there is no text, there is nothing to do
1084 if (_text == null || _text.length() == 0)
1085 return;
1086
1087 AttributedString paragraphText = new AttributedString(_text.toString());
1088 paragraphText.addAttribute(TextAttribute.FONT, getPaintFont());
1089 _lineBreaker = new LineBreakMeasurer(paragraphText.getIterator(),
1090 new FontRenderContext(null, true, true));
1091
1092 float width = Float.MAX_VALUE;
1093
1094 if (limitWidth) {
1095 if (getWidth() > 0)
1096 width = getWidth();
1097 else if (getMaxSize() != null)
1098 width = Math.max(50, getMaxSize().width - getX()
1099 - Item.MARGIN_RIGHT);
1100 }
1101
1102 _textLayouts.clear();
1103 _lineOffsets.clear();
1104 // the first line always has a 0 offset
1105 _lineOffsets.add(0);
1106
1107 TextLayout layout;
1108 _lineBreaker.setPosition(0);
1109
1110 // --- Get the output of the LineBreakMeasurer and store it in a
1111 while ((layout = _lineBreaker.nextLayout(width)) != null) {
1112 // for some reason lineBreaker will not break on newline
1113 // characters so they have to be check manually
1114 int start = _lineOffsets.get(_lineOffsets.size() - 1);
1115
1116 // check through the current line for newline characters
1117 for (int i = start + 1; i < _text.length(); i++) {
1118 if (_text.charAt(i) == '\n') {// || c == '\t'){
1119 _lineBreaker.setPosition(start);
1120 layout = _lineBreaker.nextLayout(width, i, false);
1121 break;
1122 }
1123 }
1124
1125 _lineOffsets.add(_lineBreaker.getPosition());
1126
1127 if (getWidth() > 0 && getJustification() == Justification.full
1128 && _lineBreaker.getPosition() < _text.length())
1129 layout = layout.getJustifiedLayout(width);
1130
1131 _textLayouts.add(layout);
1132 }
1133
1134 updatePolygon();
1135 }
1136
1137 private void rejustify() {
1138 // if there is no text, there is nothing to do
1139 if (_text == null || _text.length() == 0)
1140 return;
1141
1142 // only recreate linebreaker if necessary
1143 if (_lineBreaker == null) {
1144 rebuild(true);
1145 return;
1146
1147 /*
1148 * AttributedString paragraphText = new
1149 * AttributedString(_text.toString());
1150 * paragraphText.addAttribute(TextAttribute.FONT, getPaintFont());
1151 * _lineBreaker = new LineBreakMeasurer(paragraphText.getIterator(),
1152 * new FontRenderContext(null, true, true));
1153 */
1154 }
1155
1156 float width = Float.MAX_VALUE;
1157 if (getWidth() > 0)
1158 width = getWidth();
1159 else if (getMaxSize() != null)
1160 width = Math.max(50, getMaxSize().width - getX()
1161 - Item.MARGIN_RIGHT);
1162
1163 _textLayouts.clear();
1164 _lineOffsets.clear();
1165 // the first line always has a 0 offset
1166 _lineOffsets.add(0);
1167
1168 TextLayout layout;
1169 _lineBreaker.setPosition(0);
1170
1171 // --- Get the output of the LineBreakMeasurer and store it in a
1172 while ((layout = _lineBreaker.nextLayout(width)) != null) {
1173 // for some reason lineBreaker will not break on newline
1174 // characters
1175 // so they have to be check manually
1176 int start = _lineOffsets.get(_lineOffsets.size() - 1);
1177
1178 // check through the current line for newline characters
1179 for (int i = start + 1; i < _text.length(); i++) {
1180 if (_text.charAt(i) == '\n') {// || c == '\t'){
1181 _lineBreaker.setPosition(start);
1182 layout = _lineBreaker.nextLayout(width, i, false);
1183 break;
1184 }
1185 }
1186
1187 _lineOffsets.add(_lineBreaker.getPosition());
1188
1189 if (getWidth() > 0 && getJustification() == Justification.full
1190 && _lineBreaker.getPosition() < _text.length())
1191 layout = layout.getJustifiedLayout(width);
1192
1193 _textLayouts.add(layout);
1194 }
1195
1196 updatePolygon();
1197 }
1198
1199 private int _alpha = -1;
1200
1201 public void setAlpha(int alpha) {
1202 _alpha = alpha;
1203 }
1204
1205 private Point getSelectedRange(int line) {
1206 if (_selectionEnd >= _text.length()) {
1207 _selectionEnd = _text.length();
1208 }
1209
1210 if (_selectionStart < 0)
1211 _selectionStart = 0;
1212
1213 if (_selectionStart < 0 || _selectionEnd < 0)
1214 return null;
1215
1216 int selectionLeft = Math.min(_selectionStart, _selectionEnd);
1217 int selectionRight = Math.max(_selectionStart, _selectionEnd);
1218
1219 // if the selection is after this line, return null
1220 if (_lineOffsets.get(line) > selectionRight)
1221 return null;
1222
1223 // if the selection is before this line, return null
1224 if (_lineOffsets.get(line) < selectionLeft
1225 && _lineOffsets.get(line)
1226 + _textLayouts.get(line).getCharacterCount() < selectionLeft)
1227 return null;
1228
1229 // Dont highlight a single char
1230 // if (selectionRight - selectionLeft <= MINIMUM_RANGED_CHARS)
1231 // return null;
1232
1233 // the selection occurs on this line, determine where it lies on the
1234 // line
1235 int start = Math.max(0, selectionLeft - _lineOffsets.get(line));
1236 // int end = Math.min(_lineOffsets.get(line) +
1237 // _textLayouts.get(line).getCharacterCount(), _selectionEnd);
1238 int end = Math.min(selectionRight - _lineOffsets.get(line),
1239 +_textLayouts.get(line).getCharacterCount());
1240
1241 // System.out.println(line + ": " + start + "x" + end + " (" +
1242 // _selectionStart + "x" + _selectionEnd + ")");
1243 return new Point(start, end);
1244 }
1245
1246 @Override
1247 public void paint(Graphics2D g) {
1248 // if there is no text to paint, do nothing.
1249 if (_text == null || _text.length() == 0 || getMaxSize() == null)// ||
1250 // _textLayouts.size()
1251 // < 1)
1252 return;
1253
1254 if (_textLayouts.size() < 1) {
1255 rebuild(true);
1256 System.out.println("Error: " + _text);
1257 return;
1258 }
1259
1260 // the background is only cleared if required
1261 if (getBackgroundColor() != null) {
1262 Color bgc = getBackgroundColor();
1263 if (_alpha > 0)
1264 bgc = new Color(bgc.getRed(), bgc.getGreen(), bgc.getBlue(),
1265 _alpha);
1266 g.setColor(bgc);
1267 g.fill(getPolygon());
1268 }
1269
1270 float y = getY();
1271 Color c = getPaintColor();
1272 if (_alpha > 0)
1273 c = new Color(c.getRed(), c.getGreen(), c.getBlue(), _alpha);
1274
1275 g.setColor(c);
1276
1277 Color selection;
1278
1279 /*
1280 * Color main = getPaintColor(); Color back = getPaintBackgroundColor();
1281 *
1282 * if (Math.abs(main.getRed() - back.getRed()) < 10 &&
1283 * Math.abs(main.getGreen() - back.getGreen()) < 10 &&
1284 * Math.abs(main.getBlue() - back.getBlue()) < 10) { selection = new
1285 * Color(Math.abs(255 - main.getRed()), Math .abs(255 -
1286 * main.getGreen()), Math.abs(255 - main.getBlue())); } else { selection =
1287 * new Color((main.getRed() + (back.getRed() * 2)) / 3, (main.getGreen() +
1288 * (back.getGreen() * 2)) / 3, (main .getBlue() + (back.getBlue() * 2)) /
1289 * 3); }
1290 */
1291 int green = 160;
1292 int red = 160;
1293 int blue = 160;
1294
1295 if (FrameMouseActions.wasDeleteClicked()) {
1296 green = 235;
1297 red = 235;
1298 blue = 140;
1299 } else if (FrameMouseActions.getLastMouseButton() == MouseEvent.BUTTON1) {
1300 red = 255;
1301 } else if (FrameMouseActions.getLastMouseButton() == MouseEvent.BUTTON2) {
1302 green = 255;
1303 } else if (FrameMouseActions.getLastMouseButton() == MouseEvent.BUTTON3) {
1304 blue = 255;
1305 }
1306 selection = new Color(red, green, blue);
1307
1308 // width -= getX();
1309 // int line = 0;
1310 // boolean tab = false;
1311 synchronized (_textLayouts) {
1312 for (int i = 0; i < _textLayouts.size(); i++) {
1313 TextLayout layout = _textLayouts.get(i);
1314
1315 Point p = getSelectedRange(i);
1316 if (p != null) {
1317 AffineTransform at = new AffineTransform();
1318 AffineTransform orig = g.getTransform();
1319 at.translate(getX() + getJustOffset(layout), y);
1320 g.setTransform(at);
1321
1322 g.setColor(selection);
1323 g.fill(layout.getLogicalHighlightShape(p.x, p.y));
1324
1325 g.setTransform(orig);
1326 g.setColor(c);
1327 }
1328
1329 layout.draw(g, getX() + getJustOffset(layout), y);
1330
1331 /*
1332 * AffineTransform at = new AffineTransform(); AffineTransform
1333 * orig = g.getTransform(); at.translate(getX() +
1334 * getJustOffset(layout), y); g.setTransform(at);
1335 * g.draw(layout.getLogicalHighlightShape(0,
1336 * layout.getCharacterCount())); g.setTransform(orig); /*
1337 * if(_text.charAt(_lineOffsets.get(line) +
1338 * (layout.getCharacterCount() - 1)) == '\t'){ tab = true; x =
1339 * (int) (getX() + x + 20 + layout.getVisibleAdvance()); }else{
1340 */
1341 y += getLineDrop(layout);
1342 /*
1343 * tab = false; }
1344 *
1345 * line++;
1346 */
1347 }
1348 }
1349
1350 paintLink(g);
1351
1352 if (isHighlighted()) {
1353 g.setColor(getPaintHighlightColor());
1354 Stroke _highlightStroke = new BasicStroke(
1355 (float) _highlightThickness, BasicStroke.CAP_BUTT,
1356 BasicStroke.JOIN_ROUND);
1357 g.setStroke(_highlightStroke);
1358 g.drawPolygon(getPolygon());
1359 }
1360 }
1361
1362 /**
1363 * Determines if this text has any text in it.
1364 *
1365 * @return True if this Item has no text in it, false otherwise.
1366 */
1367 public boolean isEmpty() {
1368 if (_text == null || _text.length() == 0)
1369 return true;
1370
1371 return false;
1372 }
1373
1374 @Override
1375 public Polygon getPolygon() {
1376 updatePolygon();
1377
1378 Polygon external = new Polygon(_poly.xpoints, _poly.ypoints,
1379 _poly.npoints);
1380 external.translate(getX(), getY());
1381
1382 return external;
1383 }
1384
1385 @Override
1386 public Text copy() {
1387 Text copy = new Text(getID());
1388
1389 // copy standard item values
1390 Item.DuplicateItem(this, copy);
1391
1392 // copy values specific to text items
1393 copy.setSpacing(getSpacing());
1394 copy.setInitialSpacing(getInitialSpacing());
1395 copy.setJustification(getJustification());
1396 copy.setLetterSpacing(getLetterSpacing());
1397 copy.setWordSpacing(getWordSpacing());
1398 copy.setWidth(getWidth());
1399 copy.setFont(getFont());
1400 copy.setText(_text.toString());
1401 // copy._poly = new Polygon(_poly.xpoints,_poly.ypoints, _poly.npoints);
1402
1403 return copy;
1404 }
1405
1406 @Override
1407 public int getSize() {
1408 return getPaintFont().getSize();
1409 }
1410
1411 /**
1412 * Returns the number of characters in this Text, excluding new lines.
1413 *
1414 * @return The sum of the length of each line of text
1415 */
1416 public int getLength() {
1417 return _text.length();
1418 }
1419
1420 @Override
1421 public void setSize(int size) {
1422 // Dont want to have size set when duplicating a point which has size 0
1423 if (size < 0)
1424 return;
1425
1426 if (size < MINIMUM_FONT_SIZE)
1427 size = MINIMUM_FONT_SIZE;
1428 setFont(getPaintFont().deriveFont((float) size));
1429 }
1430
1431 @Override
1432 public void setAnnotation(boolean val) {
1433 if (val) {
1434 // if this is already an annotation, do nothing
1435 if (isAnnotation())
1436 return;
1437 if (_text.charAt(0) == '*') {
1438 deleteChar(0, true);
1439 if (_text.length() > 0 && _text.charAt(0) == ' ')
1440 deleteChar(0, true);
1441 } else {
1442 insertString("@", 0);
1443 }
1444 } else {
1445 // if this is not an annotation, do nothing
1446 if (!isAnnotation())
1447 return;
1448
1449 if (_text.charAt(0) == '@') {
1450 _text.setCharAt(0, ' ');
1451 insertString("*", 0);
1452 } else {
1453 deleteChar(0, true);
1454 }
1455 }
1456 rebuild(false);
1457 }
1458
1459 /**
1460 *
1461 */
1462 private void insertString(String toInsert, int pos) {
1463 assert (toInsert.length() > 0);
1464
1465 _text.insert(pos, toInsert);
1466
1467 if (toInsert.length() > 1) {
1468 _lineBreaker = null;
1469 }
1470
1471 if (_lineBreaker != null) {
1472 AttributedString inserting = new AttributedString(_text.toString());
1473 inserting.addAttribute(TextAttribute.FONT, getPaintFont());
1474 _lineBreaker.insertChar(inserting.getIterator(), pos);
1475 }
1476 }
1477
1478 private void deleteChar(int pos, boolean replaceWithDot) {
1479 _text.deleteCharAt(pos);
1480
1481 if (_text.length() == 0) {
1482 if (replaceWithDot) {
1483 // Remove and replace with a dot
1484 FrameKeyboardActions.replaceText(this);
1485 DisplayIO.setCursorPosition(this.getPosition());
1486 }
1487 return;
1488 }
1489
1490 if (_lineBreaker != null) {
1491 AttributedString inserting = new AttributedString(_text.toString());
1492 inserting.addAttribute(TextAttribute.FONT, getPaintFont());
1493 _lineBreaker.deleteChar(inserting.getIterator(), pos);
1494 }
1495 }
1496
1497 @Override
1498 public boolean isAnnotation() {
1499 if (_text != null && _text.length() > 0 && _text.charAt(0) == '@')
1500 return true;
1501
1502 return false;
1503 }
1504
1505 public boolean isSpecialAnnotation() {
1506 assert _text != null;
1507 String s = _text.toString().toLowerCase();
1508 if (s.length() > 0 && s.indexOf("@") == 0) {
1509 if (s.equals("@old") || s.equals("@ao")
1510 || s.equals("@itemtemplate") || s.equals("@parent")
1511 || s.equals("@more") || s.equals("@next")
1512 || s.equals("@previous") || s.equals("@i")
1513 || s.equals("@f"))
1514 return true;
1515 }
1516
1517 return false;
1518 }
1519
1520 @Override
1521 public Item merge(Item merger, int mouseX, int mouseY) {
1522
1523 if (merger.isLineEnd()) {
1524 // Merging line ends onto non line end text is a no-op
1525 if (!isLineEnd())
1526 return null;
1527
1528 if (merger instanceof Text)
1529 insertText(((Text) merger).getText(), mouseX, mouseY);
1530
1531 // Set the position by moving the cursor before calling this
1532 // method!!
1533
1534 List<Line> lines = new LinkedList<Line>();
1535 lines.addAll(merger.getLines());
1536 for (Line line : lines) {
1537 line.replaceLineEnd(merger, this);
1538 }
1539 merger.delete();
1540 this.setOffset(0, 0);
1541 return null;
1542 }
1543
1544 if (!(merger instanceof Text))
1545 return merger;
1546
1547 Text merge = (Text) merger;
1548
1549 // insertText(merge.getText(), mouseX, mouseY);
1550
1551 // if the item being merged has a link
1552 if (merge.getLink() != null) {
1553 // if this item has a link, keep it on the cursor
1554 if (getLink() != null) {
1555 merge.setText(merge.getLink());
1556 merge.setLink(null);
1557 // return merge;
1558 // TODO get this to return the merged item and attach it to the
1559 // cursor only when the user presses the middle button.
1560 } else
1561 setLink(merge.getLink());
1562 }
1563
1564 return null;
1565 }
1566
1567 /**
1568 * Resets the position of the item to the default position for a title item.
1569 *
1570 */
1571 public void resetTitlePosition() {
1572 setPosition(MARGIN_LEFT, MARGIN_LEFT + getBoundsHeight());
1573 }
1574
1575 /**
1576 * Removes the set of characters up to the first space in this text item.
1577 *
1578 * @return the string that was removed.
1579 */
1580 public String stripFirstWord() {
1581 int firstSpace = _text.toString().indexOf(' ');
1582
1583 // if there is only one word just make it blank
1584 if (firstSpace < 0 || firstSpace + 1 >= _text.length()) {
1585 String text = _text.toString();
1586 setText("");
1587 return text;
1588 }
1589
1590 String firstWord = _text.toString().substring(0, firstSpace);
1591 setText(_text.toString().substring(firstSpace).trim());
1592
1593 return firstWord;
1594 }
1595
1596 public String toString() {
1597 String message = "[" + getFirstLine() + "]" + FRAME_NAME_SEPARATOR;
1598
1599 if (getParent() != null)
1600 return message + getParent().getName();
1601 return message + getDateCreated();
1602 }
1603
1604 public Text getTemplateForm() {
1605 Text template = this.copy();
1606 template.setID(-1);
1607 // The template must have text otherwise the bounds height will be
1608 // zero!!
1609 // This will stop escape drop down from working if there is no item
1610 // template
1611 template.setText("@");
1612 return template;
1613 }
1614
1615 @Override
1616 public boolean isNear(int x, int y) {
1617 if (super.isNear(x, y)) {
1618 // TODO check that it is actually near one of the lines of space
1619 // return contains(x, y, getGravity() * 2 + NEAR_DISTANCE);
1620 // at the moment contains ignores gravity when checking the top and
1621 // bottom of text lines... so the cursor must be between two text
1622 // lines
1623 int textY = getY();
1624 int textX = getX();
1625
1626 for (TextLayout text : _textLayouts) {
1627 // check left and right of each box
1628 Rectangle textOutline = text.getLogicalHighlightShape(0,
1629 text.getCharacterCount()).getBounds();
1630
1631 // check if the cursor is within the top, bottom and within the
1632 // gravity of right
1633 if (y - textY > textOutline.y - NEAR_DISTANCE
1634 && y - textY < textOutline.y + textOutline.height
1635 + NEAR_DISTANCE
1636 && x - textX < textOutline.width + NEAR_DISTANCE)
1637 return true;
1638 textY += getLineDrop(text);
1639 }
1640 }
1641 return false;
1642 }
1643
1644 @Override
1645 public void delete() {
1646 setVisible(false);
1647 }
1648
1649 @Override
1650 public void anchor() {
1651 super.anchor();
1652 // ensure all text items have their selection cleared
1653 clearSelection();
1654 setAlpha(0);
1655 }
1656
1657 public void resetFrameNamePosition() {
1658 Dimension maxSize = FrameGraphics.getMaxFrameSize();
1659 setMaxSize(maxSize);
1660 if (maxSize != null) {
1661 setPosition(maxSize.width - getBoundsWidth(), getBoundsHeight());
1662 }
1663 }
1664
1665 @Override
1666 protected int getLinkYOffset() {
1667 return Math.round(-(_textLayouts.get(0).getAscent() / 2));
1668 }
1669
1670 @Override
1671 public String getName() {
1672 return getFirstLine();
1673 }
1674
1675 public static final String TAB_STRING = " ";
1676
1677 public Point insertTab(char ch, int mouseX, int mouseY) {
1678 return insertText("" + ch, mouseX, mouseY);
1679 }
1680
1681 public Point removeTab(char ch, int mouseX, int mouseY) {
1682 // Insert a space as a flag that it is a backwards tab
1683 return insertText(ch + " ", mouseX, mouseY);
1684 }
1685}
Note: See TracBrowser for help on using the repository browser.