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

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

added functionality for dockable @v's

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