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

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

Fixed some stuff with the SIMPLE debugger
Refactored the way the interpretor works to make it easier to give better error messages.

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