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

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

Added parsing of annotation items

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[] _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 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 _processedText = 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.