source: trunk/src/org/expeditee/items/Item.java@ 133

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

Turned search actions into search agents so they are run on a different thread

File size: 56.0 KB
Line 
1package org.expeditee.items;
2
3import java.awt.BasicStroke;
4import java.awt.Color;
5import java.awt.Cursor;
6import java.awt.Dimension;
7import java.awt.Graphics2D;
8import java.awt.Point;
9import java.awt.Polygon;
10import java.awt.Rectangle;
11import java.awt.Stroke;
12import java.awt.geom.AffineTransform;
13import java.awt.geom.Area;
14import java.awt.geom.Point2D;
15import java.awt.geom.Rectangle2D;
16import java.util.ArrayList;
17import java.util.Collection;
18import java.util.ConcurrentModificationException;
19import java.util.HashSet;
20import java.util.LinkedHashSet;
21import java.util.LinkedList;
22import java.util.List;
23
24import org.expeditee.actions.Actions;
25import org.expeditee.actions.Simple;
26import org.expeditee.gui.DisplayIO;
27import org.expeditee.gui.Frame;
28import org.expeditee.gui.FrameGraphics;
29import org.expeditee.gui.FrameIO;
30import org.expeditee.gui.FrameKeyboardActions;
31import org.expeditee.gui.FrameUtils;
32import org.expeditee.gui.MessageBay;
33import org.expeditee.gui.Overlay;
34import org.expeditee.gui.Vector;
35import org.expeditee.io.Conversion;
36import org.expeditee.io.Logger;
37import org.expeditee.simple.Context;
38import org.expeditee.stats.AgentStats;
39
40/**
41 * Represents everything that can be drawn on the screen (text, lines, dots,
42 * images). Each specific type is a subclass of Item.
43 *
44 * @author jdm18
45 *
46 */
47public abstract class Item implements Comparable<Item>, Runnable {
48
49 public static final Float DEFAULT_THICKNESS = 2f;
50
51 public static final Float MINIMUM_THICKNESS = 1f;
52
53 protected final int JOIN = BasicStroke.JOIN_ROUND;
54
55 protected final int CAP = BasicStroke.CAP_BUTT;
56
57 protected final Stroke HIGHLIGHT_STROKE = new BasicStroke(
58 MINIMUM_THICKNESS, CAP, JOIN, 4.0F);
59
60 // contains all dots (including this one) that form an enclosure
61 // if this dot is part of an enclosing shape
62 private Collection<Item> _enclosure = null;
63
64 public static final int LEFT_MARGIN = 13;
65
66 // indicates which end the arrowhead should be drawn at
67 protected Polygon _poly = null;
68
69 protected boolean _connectedToAnnotation = false;
70
71 public static final int NEAR_DISTANCE = 15;
72
73 /**
74 * The default Color to draw highlighting in
75 */
76 public static final int DEFAULT_HIGHLIGHT_THICKNESS = 2;
77
78 public static final Color DEFAULT_HIGHLIGHT = Color.RED;
79
80 public static final Color DEPRESSED_HIGHLIGHT = Color.GREEN;
81
82 public static final Color ALTERNATE_HIGHLIGHT = Color.BLUE;
83
84 public static final Color LINK_COLOR = Color.BLACK;
85
86 public static final Color ACTION_COLOR = Color.BLACK;
87
88 public static final Color LINK_ACTION_COLOR = Color.RED;
89
90 public static final Color DEFAULT_FOREGROUND = Color.BLACK;
91
92 public static final Color DEFAULT_BACKGROUND = Color.white;
93
94 public static final Color TRANSPARENT = new Color(0, 0, 0, 0);
95
96 /**
97 * The number of pixels highlighting should extend around Items.
98 */
99 public static final int XGRAVITY = 3;
100
101 public static final int MARGIN_RIGHT = 2;
102
103 public static final int MARGIN_LEFT = 15;
104
105 protected static final double DEFAULT_ARROWHEAD_RATIO = 0.5;
106
107 public static final Color GREEN = Color.GREEN.darker();
108
109 /**
110 * The Colors cycled through when using function keys to set the Color of
111 * this Item.
112 */
113 public static Color[] COLOR_WHEEL = { Color.BLACK, Color.RED, Color.BLUE,
114 Item.GREEN, Color.MAGENTA, Color.YELLOW.darker(), Color.WHITE };
115
116 // TODO Have shift toggle through a black and white color wheel?
117 public static Color[] FILL_COLOR_WHEEL = { new Color(255, 150, 150),
118 new Color(150, 150, 255), new Color(150, 255, 150),
119 new Color(255, 150, 255), new Color(255, 255, 100), Color.WHITE,
120 Color.BLACK };
121
122 public static final int UNCHANGED_CURSOR = -100;
123
124 public static final int DEFAULT_CURSOR = Cursor.DEFAULT_CURSOR;
125
126 public static final int HIDDEN_CURSOR = Cursor.CUSTOM_CURSOR;
127
128 public static final int TEXT_CURSOR = Cursor.TEXT_CURSOR;
129
130 public static final int CROP_CURSOR = Cursor.CROSSHAIR_CURSOR;
131
132 // The default value for integer attributes
133 public static final int DEFAULT_INTEGER = -1;
134
135 public static final int POINTTYPE_SQUARE = -1;
136
137 public static final int POINTTYPE_CIRCLE = 0;
138
139 public static void DuplicateItem(Item source, Item dest) {
140 dest.setX(source.getX());
141 dest.setY(source.getY());
142
143 dest.setActions(source.getAction());
144 dest.setActionCursorEnter(source.getActionCursorEnter());
145 dest.setActionCursorLeave(source.getActionCursorLeave());
146 dest.setActionEnterFrame(source.getActionEnterFrame());
147 dest.setActionLeaveFrame(source.getActionLeaveFrame());
148 dest.setActionMark(source.getActionMark());
149
150 dest.setBackgroundColor(source.getBackgroundColor());
151 dest.setBottomShadowColor(source.getBottomShadowColor());
152 dest.setColor(source.getColor());
153
154 dest.setData(source.getData());
155 dest.setTag(source.getTag());
156 dest.setFillColor(source.getFillColor());
157 dest.setFillPattern(source.getFillPattern());
158
159 dest.setHighlight(source.getHighlight());
160 dest.setLink(source.getLink());
161 dest.setLinkFrameset(source.getLinkFrameset());
162 dest.setLinkMark(source.getLinkMark());
163 dest.setLinkTemplate(source.getLinkTemplate());
164
165 dest.setMaxSize(source.getMaxSize());
166 dest.setOffset(source.getOffset());
167 dest.setOwner(source.getOwner());
168 dest.setThickness(source.getThickness());
169 dest.setSize(source.getSize());
170 dest.setTopShadowColor(source.getTopShadowColor());
171 dest.setLinePattern(source.getLinePattern());
172
173 dest.setFloating(source.isFloating());
174 dest.setArrow(source.getArrowheadLength(), source.getArrowheadRatio());
175
176 dest.setParent(source.getParent());
177 dest._mode = source._mode;// SelectedMode.None;
178 dest._visible = source._visible;
179 Frame parent = source.getParentOrCurrentFrame();
180 // TODO MIKE says maybe we could tighten up and only give items ID's if
181 // their
182 // current ID is negative?
183 if (parent != null) {
184 dest.setID(source.getParentOrCurrentFrame().getNextItemID());
185 }
186 // else {
187 // dest.setID(source.getID());
188 // }
189 }
190
191 public static int getGravity() {
192 return org.expeditee.gui.UserSettings.Gravity;
193 }
194
195 public static boolean showLineHighlight() {
196 return org.expeditee.gui.UserSettings.LineHighlight;
197 }
198
199 public enum HighlightMode {
200 None, Enclosed, Connected, Disconnect, Normal
201 }
202
203 public void setHighlightMode(HighlightMode mode) {
204 setHighlightMode(mode, DEFAULT_HIGHLIGHT);
205 }
206
207 protected HighlightMode _mode = HighlightMode.None;
208
209 private Point _offset = new Point(0, 0);
210
211 protected float _x;
212
213 protected float _y;
214
215 private int _id;
216
217 private String _creationDate = null;
218
219 private boolean _linkMark = true;
220
221 private boolean _actionMark = true;
222
223 private boolean _highlight = true;
224
225 private Dimension _maxSize = null;
226
227 private String _owner = null;
228
229 private String _link = null;
230
231 private StringBuffer _tag = new StringBuffer();
232
233 private List<String> _actionCursorEnter = null;
234
235 private List<String> _actionCursorLeave = null;
236
237 private List<String> _actionEnterFrame = null;
238
239 private List<String> _actionLeaveFrame = null;
240
241 public Permission _permission = Permission.full;
242
243 public void setPermission(Permission permission) {
244 _permission = permission;
245 }
246
247 public boolean hasPermission(Permission permission) {
248 return _permission.ordinal() >= permission.ordinal();
249 }
250
251 // A fill color of null represents transparent
252 private Color _colorFill = null;
253
254 // A fore color of null represents the default color
255 private Color _color = null;
256
257 protected Color _highlightColor = DEFAULT_HIGHLIGHT;
258
259 private Color _colorBackground = null;
260
261 private Color _colorTopShadow = null;
262
263 private Color _colorBottomShadow = null;
264
265 // the link\action circle
266 private Polygon _circle = null;
267
268 // the invalid link cross
269 private Polygon _circleCross = null;
270
271 private Frame _parent = null;
272
273 protected int _highlightThickness = 2;
274
275 // arrowhead parameters
276 private float _arrowheadLength = 0;
277
278 private double _arrowheadRatio = DEFAULT_ARROWHEAD_RATIO;
279
280 private Polygon _arrowhead = null;
281
282 // the list of lines that this point is part of.
283 private List<Line> _lines = new ArrayList<Line>();
284
285 private int[] _linePattern = null;
286
287 private boolean _floating = false;
288
289 // list of points constrained with this point
290 private List<Constraint> _constraints = new ArrayList<Constraint>();
291
292 private List<String> _actions = null;
293
294 private List<String> _data = null;
295
296 private String _link_frameset = null;
297
298 private String _link_template = null;
299
300 private String _fillPattern = null;
301
302 private boolean _visible = true;
303
304 private float _thickness = -1.0F;
305
306 protected Item() {
307 _creationDate = Logger.EasyDateFormat("ddMMMyyyy:HHmm");
308 }
309
310 /**
311 * Adds an action to this Item.
312 *
313 * @param action
314 * The action to add to this Item
315 */
316 public void addAction(String action) {
317 if (action == null || action.equals("")) {
318 return;
319 }
320
321 if (_actions == null) {
322 _actions = new LinkedList<String>();
323 }
324 _actions.add(action);
325 if (_actions.size() == 1) {
326 _poly = null;
327 invalidateCommonTrait(ItemAppearence.LinkChanged);
328 }
329 }
330
331 public void addAllConnected(Collection<Item> connected) {
332 if (!connected.contains(this))
333 connected.add(this);
334
335 for (Item item : getConnected()) {
336 if (!connected.contains(item))
337 item.addAllConnected(connected);
338 }
339 }
340
341 /**
342 * Adds the given Constraint to this Dot
343 *
344 * @param c
345 * The Constraint to set this Dot as a member of.
346 */
347 public void addConstraint(Constraint c) {
348 // do not add duplicate constraint
349 if (_constraints.contains(c))
350 return;
351
352 _constraints.add(c);
353 }
354
355 /**
356 * Adds a given line to the list of lines that this Point is an end for.
357 *
358 * @param line
359 * The Line that this Point is an end of.
360 */
361 public void addLine(Line line) {
362 if (_lines.contains(line)) {
363 return;
364 }
365
366 _lines.add(line);
367 }
368
369 /**
370 * Items are sorted by their Y coordinate on the screen.
371 *
372 * @param i
373 * The Item to compare this Item to
374 * @return a negative integer, zero, or a positive integer as this object is
375 * less than, equal to, or greater than the specified object.
376 */
377 public int compareTo(Item i) {
378 return getY() - i.getY();
379 }
380
381 /**
382 * Every Item has an area around it defined by a Shape (typically a
383 * rectangle), this method returns true if the given x,y pair lies within
384 * the area and false otherwise.
385 *
386 * @param x
387 * The x coordinate to check
388 * @param y
389 * The y coordinate to check
390 * @return True if the Shape around this Item contains the given x,y pair,
391 * false otherwise.
392 */
393 public boolean contains(int x, int y) {
394 return getPolygon().contains(x, y);
395 }
396
397 /**
398 * Returns a deep copy of this Item, note: it is up to the receiver to
399 * change the Item ID etc as necessary.
400 *
401 * @return A deep copy of this Item.
402 */
403 public abstract Item copy();
404
405 public void delete() {
406 _deleted = true;
407 }
408
409 @Override
410 public boolean equals(Object o) {
411 if (o == null)
412 return false;
413 if (getClass().equals(o.getClass())) {
414 Item i = (Item) o;
415 return i.getID() == getID()
416 && ((i.getParent() == _parent) || (i.getParent() != null && i
417 .getParent().equals(_parent)));
418 } else
419 return false;
420 }
421
422 /**
423 * Returns a list of any action code that is currently associated with this
424 * Item
425 *
426 * @return A List of action code associated with this Item, or null if none
427 * has been assigned.
428 */
429 public List<String> getAction() {
430 return _actions;
431 }
432
433 public List<String> getData() {
434 return _data;
435 }
436
437 public List<String> getActionCursorEnter() {
438 return _actionCursorEnter;
439 }
440
441 public List<String> getActionCursorLeave() {
442 return _actionCursorLeave;
443 }
444
445 public List<String> getActionEnterFrame() {
446 return _actionEnterFrame;
447 }
448
449 public List<String> getActionLeaveFrame() {
450 return _actionLeaveFrame;
451 };
452
453 public boolean getActionMark() {
454 return _actionMark;
455 }
456
457 /**
458 * Gets all the items connected to this item. Uses a recursive approach to
459 * search connected points.
460 *
461 * @return
462 */
463 public Collection<Item> getAllConnected() {
464 Collection<Item> list = new LinkedHashSet<Item>();
465 addAllConnected(list);
466 return list;
467 }
468
469 public Area getArea() {
470 return new Area(getPolygon());
471 }
472
473 public String getArrow() {
474 if (!hasVisibleArrow())
475 return null;
476
477 String ratio = "" + getArrowheadRatio();
478 if (ratio.length() - ratio.indexOf(".") > 2)
479 ratio = ratio.substring(0, ratio.indexOf(".") + 3);
480
481 return getArrowheadLength() + " " + ratio;
482 }
483
484 public Polygon getArrowhead() {
485 return _arrowhead;
486 }
487
488 public float getArrowheadLength() {
489 return _arrowheadLength;
490 }
491
492 public double getArrowheadRatio() {
493 return _arrowheadRatio;
494 }
495
496 public Color getBackgroundColor() {
497 return _colorBackground;
498 }
499
500 /**
501 * Returns the Color being used to shade the bottom half of this Item's
502 * border. This can be NULL if no Color is being used
503 *
504 * @return The Color displayed on the bottom\right half of this Item's
505 * border.
506 */
507 public Color getBottomShadowColor() {
508 return _colorBottomShadow;
509 }
510
511 /**
512 * Returns the height (in pixels) of this Item's surrounding area.
513 *
514 * @return The height (in pixels) of this Item's surrounding area as
515 * returned by getArea().
516 */
517 public int getBoundsHeight() {
518 return getPolygon().getBounds().height;
519 }
520
521 /**
522 * Returns the width (in pixels) of this Item's surrounding area.
523 *
524 * @return The width (in pixels) of this Item's surrounding area as returned
525 * by getArea().
526 */
527 public int getBoundsWidth() {
528 return getPolygon().getBounds().width;
529 }
530
531 // TODO draw the link with a circle rather than a polygon!!
532 protected Polygon getLinkPoly() {
533 if (_circle == null) {
534 int points = 16;
535
536 double radians = 0.0;
537 int xPoints[] = new int[points];
538 int yPoints[] = new int[xPoints.length];
539
540 for (int i = 0; i < xPoints.length; i++) {
541 // circle looks best if these values are not related to gravity
542 xPoints[i] = (int) (3.5 * Math.cos(radians)) + 6;// (2 *
543 // GRAVITY);
544 yPoints[i] = (int) (3.5 * Math.sin(radians)) + 3;// GRAVITY;
545 radians += (2.0 * Math.PI) / xPoints.length;
546 }
547
548 _circle = new Polygon(xPoints, yPoints, xPoints.length);
549 }
550
551 return _circle;
552 }
553
554 protected Polygon getCircleCross() {
555
556 if (_circleCross == null) {
557 _circleCross = new Polygon();
558
559 Rectangle bounds = getLinkPoly().getBounds();
560 int x1 = (int) bounds.getMinX();
561 int x2 = (int) bounds.getMaxX();
562 int y1 = (int) bounds.getMinY();
563 int y2 = (int) bounds.getMaxY();
564 int midX = ((x2 - x1) / 2) + x1;
565 int midY = ((y2 - y1) / 2) + y1;
566
567 _circleCross.addPoint(x1, y1);
568 _circleCross.addPoint(x2, y2);
569 _circleCross.addPoint(midX, midY);
570 _circleCross.addPoint(x1, y2);
571 _circleCross.addPoint(x2, y1);
572 _circleCross.addPoint(midX, midY);
573 }
574
575 return _circleCross;
576 }
577
578 public Color getColor() {
579 return _color;
580 }
581
582 public Collection<Item> getConnected() {
583 List<Item> conn = new LinkedList<Item>();
584 conn.add(this);
585 conn.addAll(getEnclosures());
586 conn.addAll(getLines());
587 return conn;
588 }
589
590 public String getConstraintIDs() {
591 if (_constraints == null || _constraints.size() == 0)
592 return null;
593
594 String cons = "";
595
596 for (Constraint c : _constraints)
597 cons += c.getID() + " ";
598
599 return cons.trim();
600 }
601
602 /*
603 * public void setLinkValid(boolean val) { _isValidLink = val; }
604 */
605
606 /**
607 * Returns a List of any Constraints that this Dot is a memeber of.
608 *
609 * @return a List of Constraints that this Dot is a member of.
610 */
611 public List<Constraint> getConstraints() {
612 return _constraints;
613 }
614
615 public String getTag() {
616 if (_tag != null && _tag.length() > 0)
617 return _tag.toString();
618 return null;
619 }
620
621 public String getDateCreated() {
622 return _creationDate;
623 }
624
625 public Color getFillColor() {
626 return _colorFill;
627 }
628
629 public String getFillPattern() {
630 return _fillPattern;
631 }
632
633 public String getFirstAction() {
634 if (_actions == null || _actions.size() == 0)
635 return null;
636 return _actions.get(0);
637 }
638
639 public boolean getHighlight() {
640 return _highlight;
641 }
642
643 public Color getHighlightColor() {
644 if (_highlightColor.equals(getPaintColor()))
645 return ALTERNATE_HIGHLIGHT;
646 return _highlightColor;
647 }
648
649 /**
650 * Returns the ID of this Item, which must be unique for the Frame.
651 *
652 * @return The ID of this Item.
653 */
654 public int getID() {
655 return _id;
656 }
657
658 /**
659 * Returns the list of IDs of the Lines that this Dot is an end of.
660 *
661 * @return The list of Line IDs that this point is part of.
662 */
663 public String getLineIDs() {
664 String lineID = null;
665
666 if (_lines.size() > 0) {
667 lineID = "" + _lines.get(0).getID();
668
669 for (int i = 1; i < _lines.size(); i++)
670 lineID += " " + _lines.get(i).getID();
671 }
672
673 return lineID;
674 }
675
676 public int[] getLinePattern() {
677 return _linePattern;
678 }
679
680 /**
681 * Returns a list of Lines where this Dot is an end.
682 *
683 * @return A list of the Lines that this Dot is an end for or null if no
684 * Lines have been added.
685 */
686 public List<Line> getLines() {
687 return _lines;
688 }
689
690 /**
691 * Returns the name of a Frame that this Item links to, or null if this Item
692 * has no link.
693 *
694 * @return The name of a Frame that this Item links to (if any) or null if
695 * this Item does not link to anything.
696 */
697 public String getLink() {
698 return _link;
699 }
700
701 public String getLinkFrameset() {
702 return _link_frameset;
703 }
704
705 public boolean getLinkMark() {
706 return _linkMark;
707 }
708
709 public String getLinkTemplate() {
710 return _link_template;
711 }
712
713 public Dimension getMaxSize() {
714 return _maxSize;
715 }
716
717 public Point getOffset() {
718 return _offset;
719 }
720
721 public String getOwner() {
722 return _owner;
723 }
724
725 public Color getPaintBackgroundColor() {
726 Color colorBackground = getBackgroundColor();
727 if (colorBackground == null) {
728 if (getParent() != null && getParent().getBackgroundColor() != null)
729 return getParent().getBackgroundColor();
730
731 return DEFAULT_BACKGROUND;
732 }
733
734 return colorBackground;
735 }
736
737 /**
738 * Returns the foreground Color of this Item.
739 *
740 * @return The Color of this item (foreground)
741 */
742 public final Color getPaintColor() {
743 // If color is null then get the paint foregroundColor for the frame the
744 // item is on which is a color adjusted to suit the background
745 Color color = getColor();
746
747 if (color == null) {
748 if (getParent() != null)
749 return getParent().getPaintForegroundColor();
750
751 Frame current = DisplayIO.getCurrentFrame();
752 if (current == null)
753 return DEFAULT_FOREGROUND;
754
755 return current.getPaintForegroundColor();
756 }
757
758 return color;
759 }
760
761 protected Color getPaintHighlightColor() {
762 Color highlightColor = _highlightColor;
763 if (getPaintBackgroundColor().equals(highlightColor))
764 highlightColor = ALTERNATE_HIGHLIGHT;
765 if (getParent() != null
766 && getParent().getPaintBackgroundColor().equals(highlightColor))
767 highlightColor = getParent().getPaintForegroundColor();
768
769 return highlightColor;
770 }
771
772 public final Frame getParent() {
773 return _parent;
774 }
775
776 public final Point getPosition() {
777 return new Point(getX(), getY());
778 }
779
780 /**
781 * Returns the size of this Item. For Text this is the Font size, for Lines
782 * and Dots this is the thickness.
783 *
784 * @return The size of this Item.
785 */
786 public float getSize() {
787 return -1.0F;
788 }
789
790 /**
791 * Returns the Color being used to shade the top half of this Item's border.
792 * This can be NULL if no Color is being used
793 *
794 * @return The Color displayed on the top\left half of this Item's border.
795 */
796 public Color getTopShadowColor() {
797 return _colorTopShadow;
798 }
799
800 public String getTypeAndID() {
801 return "T " + getID();
802 }
803
804 public int getWidth() {
805 return 0;
806 }
807
808 /**
809 * Returns the X coordinate of this Item on the screen
810 *
811 * @return The X coordinate of this Item on the screen
812 */
813 public int getX() {
814 return Math.round(_x);
815 }
816
817 /**
818 * Returns the Y coordinate of this Item on the screen
819 *
820 * @return The Y coordinate of this Item on the screen
821 */
822 public int getY() {
823 return Math.round(_y);
824 }
825
826 protected boolean hasVisibleArrow() {
827 return isLineEnd() && getArrowheadRatio() != 0
828 && getArrowheadLength() != 0;
829 }
830
831 /**
832 * Checks if the given Shape intersects with the Shape around this Item.
833 *
834 * @param s
835 * The Shape to check.
836 * @return True if the two Shapes overlap, False otherwise.
837 */
838 public boolean intersects(Polygon p) {
839 if (p == null)
840 return false;
841
842 Area a = new Area(p);
843 a.intersect(this.getArea());
844
845 return !a.isEmpty();
846 }
847
848 /**
849 * Note: Pictures always return False, as they should be drawn even when no
850 * other annotation Items are.
851 *
852 * @return True if this Item is an annotation, False otherwise.
853 */
854 public boolean isAnnotation() {
855 return false;
856 }
857
858 public boolean isFloating() {
859 return _floating;
860 }
861
862 public boolean isFrameName() {
863 if (this.getParent() == null || this.getParent().getNameItem() != this)
864 return false;
865 return true;
866 }
867
868 public boolean isFrameTitle() {
869 if (this.getParent() == null || this.getParent().getTitleItem() != this)
870 return false;
871 return true;
872 }
873
874 /**
875 * Returns True if this Item is currently highlighted.
876 *
877 * @return True if this Item is currently highlighted on the screen, False
878 * otherwise.
879 */
880 public boolean isHighlighted() {
881 if (isFloating())
882 return false;
883 return _mode != HighlightMode.None;
884 }
885
886 /**
887 * Tests if the item link is a valid framename, that is, the String must
888 * begin with a character, end with a number with 0 or more letters and
889 * numbers in between. If there is a dot in the framename all the chars
890 * after it must be digits.
891 *
892 * @return True if the given framename is proper, false otherwise.
893 */
894 public boolean isLinkValid() {
895 if (FrameIO.isPositiveInteger(getLink()))
896 return true;
897
898 if (FrameIO.isValidFrameName(getLink()))
899 return true;
900 return false;
901 }
902
903 public boolean isNear(int x, int y) {
904
905 int xLeft = getPolygon().getBounds().x;
906 int yTop = getPolygon().getBounds().y;
907
908 return (x > xLeft - NEAR_DISTANCE && y > yTop - NEAR_DISTANCE
909 && x < xLeft + getBoundsWidth() + NEAR_DISTANCE && y < yTop
910 + getBoundsHeight() + NEAR_DISTANCE);
911 }
912
913 public boolean isOldTag() {
914 if (this instanceof Text)
915 if (((Text) this).getTextList().get(0).toLowerCase().equals("@old"))
916 return true;
917 return false;
918 }
919
920 /**
921 * Merges this Item with the given Item. The merger Item should be left
922 * unchanged after this method. The merger may or may not be the same class
923 * as this Item, exact behaviour depends on the subclass, No-op is allowed.
924 *
925 * @param merger
926 * The Item to merge with
927 * @return any Item that should remain on the cursor
928 */
929 public abstract Item merge(Item merger, int mouseX, int mouseY);
930
931 /**
932 * Displays this item directly on the screen. Note: All Items are
933 * responsible for their own drawing, buffering, etc.
934 *
935 * @param g
936 * The Graphics to draw this Item on.
937 */
938 public abstract void paint(Graphics2D g);
939
940 public void paintFill(Graphics2D g) {
941 Color fillColor = getFillColor();
942 if (fillColor != null && getEnclosingDots() != null) {
943 if (isFloating()) {
944 // TODO experiment with adding alpha when picking up filled
945 // items...
946 // Slows things down quite alot!!
947 fillColor = new Color(fillColor.getRed(), fillColor.getGreen(),
948 fillColor.getBlue());
949 }
950 g.setColor(fillColor);
951 // The painting is not efficient enough for gradients...
952 // Polygon s = getEnclosedShape();
953 // Rectangle b = s.getBounds();
954 // GradientPaint gp = new GradientPaint((int) (b.x + b.width * 0.3),
955 // b.y, getFillColor(), (int) (b.x + b.width * 1.3), b.y,
956 // Color.white);
957 // g.setPaint(gp);
958 // g.fillPolygon(s);
959 g.fillPolygon(getEnclosedShape());
960 }
961 }
962
963 /**
964 * This method performs all the actions in an items list. If it contains a
965 * link as well the link is used as the source frame for all acitons.
966 */
967 public void performActions() {
968 Frame sourceFrame = null;
969 // if a link exists make it the source frame for this action
970 if (getLink() != null) {
971 sourceFrame = FrameUtils.getFrame(getAbsoluteLink());
972 }
973 // if no link exists or the link is bad then use the
974 // currently displayed frame as the source frame for the
975 // action
976 if (sourceFrame == null) {
977 sourceFrame = DisplayIO.getCurrentFrame();
978 }
979
980 for (String s : getAction()) {
981 Actions.PerformAction(sourceFrame, this, s);
982 }
983 }
984
985 /**
986 * Removes all constraints that this item has.
987 *
988 */
989 public void removeAllConstraints() {
990 while (_constraints.size() > 0) {
991 Constraint c = _constraints.get(0);
992 c.getEnd().removeConstraint(c);
993 c.getStart().removeConstraint(c);
994 }
995 }
996
997 /**
998 * Clears the list of Lines that this Dot is an end of. Note: This only
999 * clears this Dot's list and does not have any affect on the Lines or other
1000 * Dots.
1001 */
1002 public void removeAllLines() {
1003 for (Line l : _lines) {
1004 l.invalidateAll();
1005 }
1006 _lines.clear();
1007 }
1008
1009 /**
1010 * Removes the given Constraint from the list of constraintss that this Dot
1011 * is a part of.
1012 *
1013 * @param c
1014 * The Constraint that this Dot is no longer a part of.
1015 */
1016 public void removeConstraint(Constraint c) {
1017 _constraints.remove(c);
1018 }
1019
1020 /**
1021 * Removes the given Line from the list of lines that this Dot is an end
1022 * for.
1023 *
1024 * @param line
1025 * The Line that this Dot is no longer an end of.
1026 */
1027 public void removeLine(Line line) {
1028 if (_lines.remove(line))
1029 line.invalidateAll();
1030 }
1031
1032 public void run() {
1033 try {
1034 Simple.ProgramStarted();
1035 Simple.RunFrameAndReportError(this, new Context());
1036 Simple.ProgramFinished();
1037 MessageBay.displayMessage(AgentStats.getStats(), GREEN);
1038 } catch (ConcurrentModificationException ce) {
1039 Simple.ProgramFinished();
1040 ce.printStackTrace();
1041 } catch (Exception e) {
1042 MessageBay.linkedErrorMessage(e.getMessage());
1043 Simple.ProgramFinished();
1044 }
1045 }
1046
1047 /**
1048 * Check if it has a relative link if so make it absolute.
1049 *
1050 */
1051 public void setAbsoluteLink() {
1052 String link = getLink();
1053 if (link == null)
1054 return;
1055 // Check if all the characters are digits and hence it is a relative
1056 // link
1057 if (!FrameIO.isPositiveInteger(link))
1058 return;
1059
1060 // Make it an absolute link
1061 String framesetName;
1062
1063 if (_parent == null)
1064 framesetName = DisplayIO.getCurrentFrame().getFramesetName();
1065 else
1066 framesetName = _parent.getFramesetName();
1067
1068 setLink(framesetName + link);
1069 }
1070
1071 /**
1072 * Sets any action code that should be associated with this Item Each entry
1073 * in the list is one line of code
1074 *
1075 * @param actions
1076 * The lines of code to associate with this Item
1077 */
1078 public void setActions(List<String> actions) {
1079 if (actions == null || actions.size() == 0) {
1080 invalidateCommonTrait(ItemAppearence.LinkChanged);
1081 _actions = null;
1082 } else
1083 _actions = new LinkedList<String>(actions);
1084
1085 // Want to resize the highlight box for text items if actions have been
1086 // added
1087 _poly = null;
1088 invalidateCommonTrait(ItemAppearence.LinkChanged);
1089 }
1090
1091 public void setData(List<String> data) {
1092 if (data == null || data.size() == 0)
1093 _data = null;
1094 else
1095 _data = new LinkedList<String>(data);
1096 }
1097
1098 public void setActionCursorEnter(List<String> enter) {
1099 _actionCursorEnter = enter;
1100 }
1101
1102 public void setActionCursorLeave(List<String> leave) {
1103 _actionCursorLeave = leave;
1104 }
1105
1106 public void setActionEnterFrame(List<String> enter) {
1107 _actionEnterFrame = enter;
1108 }
1109
1110 public void setActionLeaveFrame(List<String> leave) {
1111 _actionLeaveFrame = leave;
1112 }
1113
1114 public void setActionMark(boolean val) {
1115 if (!val)
1116 invalidateCommonTrait(ItemAppearence.LinkChanged);
1117 _poly = null;
1118 _actionMark = val;
1119 if (val)
1120 invalidateCommonTrait(ItemAppearence.LinkChanged);
1121 }
1122
1123 /**
1124 * Sets whether this Item is an Annotation.
1125 *
1126 * @param val
1127 * True if this Item is an Annotation, False otherwise.
1128 */
1129 public abstract void setAnnotation(boolean val);
1130
1131 /**
1132 * Used to set this Line as an Arrow. If length and ratio are 0, no arrow is
1133 * shown.
1134 *
1135 * @param length
1136 * The how far down the shaft of the line the arrowhead should
1137 * come.
1138 * @param ratio
1139 * The ratio of the arrow's length to its width.
1140 */
1141 public void setArrow(float length, double ratio) {
1142 _arrowheadLength = length;
1143 _arrowheadRatio = ratio;
1144 updateArrowPolygon();
1145 }
1146
1147 public void setArrowhead(Polygon arrow) {
1148 _arrowhead = arrow;
1149 }
1150
1151 public void setArrowheadLength(float length) {
1152 _arrowheadLength = length;
1153 updateArrowPolygon();
1154 }
1155
1156 public void setArrowheadRatio(double ratio) {
1157 _arrowheadRatio = ratio;
1158 updateArrowPolygon();
1159 }
1160
1161 public void setBackgroundColor(Color c) {
1162 if (c != _colorBackground) {
1163 _colorBackground = c;
1164 invalidateCommonTrait(ItemAppearence.BackgroundColorChanged);
1165 }
1166 }
1167
1168 /**
1169 * Sets the Color to use on the bottom and right sections of this Item's
1170 * border. If top is NULL, then the Item's background Color will be used.
1171 *
1172 * @param top
1173 * The Color to display in the bottom and right sections of this
1174 * Item's border.
1175 */
1176 public void setBottomShadowColor(Color bottom) {
1177 _colorBottomShadow = bottom;
1178 }
1179
1180 /**
1181 * Sets the foreground Color of this Item to the given Color.
1182 *
1183 * @param c
1184 */
1185 public void setColor(Color c) {
1186 if (c != _color) {
1187 _color = c;
1188 invalidateCommonTrait(ItemAppearence.ForegroundColorChanged);
1189 if (hasVector()) {
1190 // TODO make this more efficient so it only repaints the items
1191 // for this vector
1192 FrameKeyboardActions.Refresh();
1193 }
1194 }
1195 }
1196
1197 public void setConstraintIDs(String IDs) {
1198 }
1199
1200 public void setConstraints(List<Constraint> constraints) {
1201 _constraints = constraints;
1202 }
1203
1204 public void setTag(String newData) {
1205 if (newData != null)
1206 _tag = new StringBuffer(newData);
1207 else
1208 _tag = null;
1209 }
1210
1211 /**
1212 * Sets the created date of this Frame to the given String.
1213 *
1214 * @param date
1215 * The date to use for this Frame.
1216 */
1217 public void setDateCreated(String date) {
1218 _creationDate = date;
1219 }
1220
1221 public void setFillColor(Color c) {
1222
1223 _colorFill = c;
1224
1225 for (Line line : _lines) {
1226 Item other = line.getOppositeEnd(this);
1227 if (other.getFillColor() != c)
1228 other.setFillColor(c);
1229 }
1230
1231 invalidateCommonTrait(ItemAppearence.FillColor);
1232 invalidateFill();
1233 }
1234
1235 public void setFillPattern(String patternLink) {
1236 _fillPattern = patternLink;
1237 invalidateCommonTrait(ItemAppearence.FillPattern);
1238 invalidateFill();
1239 }
1240
1241 public void setFloating(boolean val) {
1242 _floating = val;
1243 }
1244
1245 public void setHighlight(boolean val) {
1246 _highlight = val;
1247 }
1248
1249 /**
1250 * Sets the ID of this Item to the given Integer. Note: Items with ID's < 0
1251 * are not saved
1252 *
1253 * @param newID
1254 * The new ID to assign this Item.
1255 */
1256 public void setID(int newID) {
1257 _id = newID;
1258 }
1259
1260 /**
1261 * Sets the list of lines that this point is part of (may be set to null).
1262 *
1263 * @param lineID
1264 * A String of line ID numbers separated by spaces.
1265 */
1266 public void setLineIDs(String lineID) {
1267 }
1268
1269 public void setLinePattern(int[] pattern) {
1270 _linePattern = pattern;
1271
1272 for (Line line : getLines())
1273 line.setLinePattern(pattern);
1274 }
1275
1276 public void setLines(List<Line> lines) {
1277 _lines = lines;
1278
1279 for (Line line : lines)
1280 line.setLinePattern(getLinePattern());
1281
1282 }
1283
1284 /**
1285 * Links this item to the given Frame, this may be set to null to remove a
1286 * link.
1287 *
1288 * @param frameName
1289 * The name of the Frame to link this item to.
1290 */
1291 public void setLink(String frameName) {
1292 if (frameName == null) {
1293 invalidateCommonTrait(ItemAppearence.LinkChanged);
1294 }
1295
1296 // If a link is being removed or set then need to reset poly so the
1297 // highlighting is drawn with the correct width
1298 if (frameName == null || getLink() == null)
1299 _poly = null;
1300
1301 if (FrameIO.isValidLink(frameName))
1302 _link = frameName;
1303 else
1304 MessageBay.errorMessage("[" + frameName
1305 + "] is not a valid frame name");
1306 // TODO make this throw exceptions etc...
1307
1308 invalidateCommonTrait(ItemAppearence.LinkChanged);
1309 }
1310
1311 public void setLinkFrameset(String frameset) {
1312 if (frameset == null || FrameIO.isValidFramesetName(frameset))
1313 _link_frameset = frameset;
1314 else
1315 MessageBay.errorMessage("[" + frameset
1316 + "] is not a valid frameset name");
1317 // TODO make this throw exceptions etc...
1318 }
1319
1320 public void setLinkMark(boolean val) {
1321 if (!val)
1322 invalidateCommonTrait(ItemAppearence.LinkChanged);
1323 _poly = null;
1324 _linkMark = val;
1325 if (val)
1326 invalidateCommonTrait(ItemAppearence.LinkChanged);
1327 }
1328
1329 public void setLinkTemplate(String template) {
1330 if (FrameIO.isValidLink(template))
1331 _link_template = template;
1332 else
1333 MessageBay.errorMessage("[" + template
1334 + "] is not a valid frame name");
1335 // TODO make this throw exceptions etc...
1336 }
1337
1338 /**
1339 * Sets the maximum coordinates on the screen that this item may occupy.
1340 * This is used by Text items to compute word-wrapping lengths.
1341 *
1342 * @param d
1343 * The Maximum size of the Frame containing this Item.
1344 */
1345 public void setMaxSize(Dimension d) {
1346 if (d != null) {
1347 _maxSize = d;
1348 updatePolygon();
1349 }
1350 }
1351
1352 public void setOffset(int x, int y) {
1353 _offset.setLocation(x, y);
1354 }
1355
1356 public void setOffset(Point p) {
1357 _offset.setLocation(p);
1358 }
1359
1360 public void setOwner(String own) {
1361 _owner = own;
1362 }
1363
1364 public void setParent(Frame frame) {
1365 _parent = frame;
1366 }
1367
1368 /**
1369 * Invalidates this, connected lines and fill
1370 *
1371 * @param trait
1372 */
1373 private void invalidateCommonTraitForAll(ItemAppearence trait) {
1374 invalidateCommonTrait(trait);
1375 for (Line line : getLines())
1376 line.invalidateCommonTrait(trait);
1377 if (_colorFill != null) {
1378 invalidateFill(); // only invalidates if has fill
1379 }
1380 for (XRayable x : getEnclosures()) {
1381 x.invalidateCommonTrait(trait);
1382 }
1383
1384 }
1385
1386 /**
1387 * Sets the position of this item on the screen
1388 *
1389 * @param x
1390 * The new X coordinate
1391 * @param y
1392 * The new Y coordinate
1393 */
1394 public void setPosition(float x, float y) {
1395 float deltaX = x - _x;
1396 float deltaY = y - _y;
1397
1398 if (deltaX == 0 && deltaY == 0)
1399 return;
1400
1401 invalidateCommonTraitForAll(ItemAppearence.PreMoved);
1402
1403 _x = x;
1404 _y = y;
1405
1406 for (Item i : getEnclosures()) {
1407 i.updatePolygon();
1408 }
1409 updatePolygon();
1410
1411 // update the position of any dots that are constrained by this one
1412 for (Constraint c : _constraints) {
1413 Item other = c.getOppositeEnd(this);
1414
1415 // only set position if the other dot is still fixed to the
1416 // frame
1417 if (/* this.isFloating() && */!other.isFloating()) {
1418 if (c.getType() == Constraint.HORIZONTAL) {
1419 if (other._y != y) {
1420 other.setY(y);
1421 }
1422 } else if (c.getType() == Constraint.VERTICAL) {
1423 if (other._x != x) {
1424 other.setX(x);
1425 }
1426 } else if (c.isDiagonal()) {
1427 if (Math.abs(other._x - x) != Math.abs(other._y - y)) {
1428
1429 float m1 = c.getGradient();
1430 float c1 = y - m1 * x;
1431 // Now work out the equation for the second line
1432 // Get the first line the other end is attached to that
1433 // is not the diagonal line
1434 List<Line> lines = other.getLines();
1435 // If there is only one line...
1436 if (lines.size() == 1) {
1437 if (m1 != 0) {
1438 if (Math.abs(deltaX) > Math.abs(deltaY)) {
1439 other.setX((other._y - c1) / m1);
1440 } else {
1441 other.setY(m1 * other._x + c1);
1442 }
1443 }
1444 } else if (lines.size() > 1) {
1445 Line otherLine = lines.get(0);
1446 Item end = otherLine.getOppositeEnd(other);
1447 if (end.equals(this)) {
1448 otherLine = lines.get(1);
1449 end = otherLine.getOppositeEnd(other);
1450 assert (!end.equals(this));
1451 }
1452
1453 float xDiff = end._x - other._x;
1454 float yDiff = end._y - other._y;
1455 if (xDiff == 0) {
1456 other.setY(m1 * other._x + c1);
1457 } else if (Math.abs(xDiff) == Math.abs(yDiff)
1458 && !this.isFloating() && deltaX == 0
1459 && deltaY == 0) {
1460 if (deltaX == 0) {
1461 _x = (_y - other._y) * m1 + other._x;
1462 } else {
1463 _y = (_x - other._x) * m1 + other._y;
1464 }
1465 } else {
1466 float m2 = yDiff / xDiff;
1467 float c2 = end._y - m2 * end._x;
1468 float mDiff = m1 - m2;
1469 if (Math.abs(mDiff) < 0.000001) {
1470 assert (false);
1471 // TODO how do I handle this case!!
1472 } else {
1473 float newX = (c2 - c1) / mDiff;
1474 float newY = m1 * newX + c1;
1475 if (other._x != newX
1476 /* && other._y != newY */) {
1477 other.setPosition(newX, newY);
1478 }
1479 }
1480 }
1481 }
1482 // Do simultaneous equations to get the new postion for
1483 // the other end of the diagonal line
1484 }
1485 }
1486 }
1487 }
1488
1489 for (Line line : getLines()) {
1490 line.updatePolygon();
1491 }
1492
1493 invalidateCommonTraitForAll(ItemAppearence.PostMoved);
1494
1495 }
1496
1497 public void setPosition(Point position) {
1498 setPosition(position.x, position.y);
1499 }
1500
1501 public void setRelativeLink() {
1502 String link = getLink();
1503 if (link == null)
1504 return;
1505 assert (_parent != null);
1506
1507 if (FrameIO.isPositiveInteger(link))
1508 return;
1509
1510 // Check if the link is for the current frameset
1511 if (_parent.getFramesetName().equalsIgnoreCase(
1512 Conversion.getFramesetName(link))) {
1513 setLink("" + Conversion.getFrameNumber(link));
1514 }
1515 }
1516
1517 /**
1518 * Sets the size of this Item. For Text this is the Font size. For Lines and
1519 * Dots this is the thickness.
1520 */
1521 public void setSize(float size) {
1522 }
1523
1524 /**
1525 * Sets the thickness of the item.
1526 *
1527 * @param thick
1528 */
1529 public void setThickness(float thick) {
1530 setThickness(thick, true);
1531 }
1532
1533 /**
1534 * Sets the thickness of this item.
1535 *
1536 * @param thick
1537 * the new thickness for the item
1538 * @param setConnectedThickness
1539 * true if all items connected to this item should also have
1540 * their thickness set
1541 */
1542 public void setThickness(float thick, boolean setConnectedThickness) {
1543 if (thick == _thickness)
1544 return;
1545 boolean bigger = thick > _thickness;
1546
1547 if (!bigger) {
1548 if (setConnectedThickness) {
1549 // TODO is there a more efficient way of doing this?
1550 for (Item i : getConnected())
1551 i.invalidateCommonTrait(ItemAppearence.Thickness);
1552 } else {
1553 invalidateCommonTrait(ItemAppearence.Thickness);
1554 }
1555 }
1556
1557 _thickness = thick;
1558 // update the size of any lines
1559 for (Line line : getLines())
1560 line.setThickness(thick, setConnectedThickness);
1561
1562 updatePolygon();
1563
1564 if (bigger) {
1565 if (setConnectedThickness) {
1566 for (Item i : getConnected())
1567 i.invalidateCommonTrait(ItemAppearence.Thickness);
1568 } else {
1569 invalidateCommonTrait(ItemAppearence.Thickness);
1570 }
1571 }
1572 }
1573
1574 /**
1575 * Returns the thickness (in pixels) of this Dot.
1576 *
1577 * @return The 'thickness' of this Dot. (returns -1 if the thickness is not
1578 * set).
1579 */
1580 public float getThickness() {
1581 return _thickness;
1582 }
1583
1584 /**
1585 * Sets the Color to use on the top and left sections of this Item's border.
1586 * If top is NULL, then the Item's background Color will be used.
1587 *
1588 * @param top
1589 * The Color to display in the top and left sections of this
1590 * Item's border.
1591 */
1592 public void setTopShadowColor(Color top) {
1593 _colorTopShadow = top;
1594 }
1595
1596 public void setWidth(int width) throws UnsupportedOperationException {
1597 throw new UnsupportedOperationException(
1598 "Item type does not support width attribute!");
1599 }
1600
1601 /**
1602 * Sets the position of this Item on the X axis
1603 *
1604 * @param newX
1605 * The position on the X axis to assign to this Item
1606 */
1607 public void setX(float newX) {
1608 setPosition(newX, getY());
1609 }
1610
1611 /**
1612 * Sets the position of this Item on the Y axis
1613 *
1614 * @param newY
1615 * The position on the Y axis to assign to this Item
1616 */
1617 public void setY(float newY) {
1618 setPosition(getX(), newY);
1619 }
1620
1621 /**
1622 * Paints any highlighting of this Item. This may include changing the
1623 * thickness (lines) or painting a box around the item (Text, Images). If
1624 * val is True then the Graphics Color is changed to the highlight Color, if
1625 * False then the Graphics Color is left unchanged (for clearing of
1626 * highlighting).
1627 *
1628 * @param val
1629 * True if this Item should be highlighted, false if the
1630 * highlighting is being cleared.
1631 * @return The desired mouse cursor when this Item is highlighted (negative
1632 * means no change)
1633 */
1634 public int setHighlightColor() {
1635 return setHighlightColor(DEFAULT_HIGHLIGHT);
1636 }
1637
1638 public int setHighlightColor(Color c) {
1639
1640 _highlightThickness = DEFAULT_HIGHLIGHT_THICKNESS;
1641
1642 Color selColor = (c != null) ? c : DEFAULT_HIGHLIGHT;
1643 if (_highlightColor != c) {
1644 _highlightColor = selColor;
1645 this.invalidateCommonTrait(ItemAppearence.HighlightColorChanged);
1646 }
1647
1648 return Item.UNCHANGED_CURSOR;
1649
1650 }
1651
1652 private void updateArrowPolygon() {
1653 if (getArrowheadLength() < 0 || getArrowheadRatio() < 0)
1654 _arrowhead = null;
1655 else {
1656 _arrowhead = new Polygon();
1657 _arrowhead.addPoint(Math.round(getX()), Math.round(getY()));
1658 _arrowhead.addPoint((Math.round(getX() - getArrowheadLength())),
1659 ((int) Math.round(getY()
1660 - (getArrowheadLength() * getArrowheadRatio()))));
1661 _arrowhead.addPoint(Math.round(getX()), (int) getY());
1662 _arrowhead
1663 .addPoint(
1664 (int) Math.round(getX() - getArrowheadLength()),
1665 (int) Math
1666 .round((getY() + (getArrowheadLength() * getArrowheadRatio()))));
1667 }
1668 }
1669
1670 public abstract void updatePolygon();
1671
1672 public void setHidden(boolean state) {
1673 this._visible = !state;
1674 }
1675
1676 public void setVisible(boolean state) {
1677 this._visible = state;
1678 }
1679
1680 public boolean isVisible() {
1681 return _visible && !_deleted;
1682 }
1683
1684 /**
1685 * Raised whenever the item is removed, added, no longer in view (That is,
1686 * when it is not on any of the current frames, of overlays of the current
1687 * frames) or has become visible. That is, when it is either on a current
1688 * frame, or an overlay of a current frame.
1689 *
1690 * @param e
1691 * The event
1692 */
1693 public void onParentStateChanged(ItemParentStateChangedEvent e) {
1694 }
1695
1696 public void setHighlightMode(HighlightMode mode, Color color) {
1697 setHighlightColor(color);
1698 if (_mode != mode) {
1699 _mode = mode;
1700 this.invalidateCommonTrait(ItemAppearence.HighlightModeChanged);
1701 }
1702 }
1703
1704 public HighlightMode getHighlightMode() {
1705 return _mode;
1706 }
1707
1708 public void anchor() {
1709 Frame current = getParentOrCurrentFrame();
1710 setID(current.getNextItemID());
1711 setOffset(0, 0);
1712 setParent(current);
1713
1714 current.addItem(this);
1715 current.setResort(true);
1716 setRelativeLink();
1717 setFloating(false);
1718
1719 // // If its an unconstrained line end check if we should add a
1720 // constraint
1721 // if (isLineEnd() && getLines().size() <= 2
1722 // && getConstraints().size() <= 1) {
1723 // Constraint existingConstraint = null;
1724 // List<Constraint> constraints = getConstraints();
1725 // // Get the existing constraint
1726 // if (constraints.size() > 0) {
1727 // existingConstraint = constraints.get(0);
1728 // }
1729 // for (Line line : getLines()) {
1730 // Integer constraintType = line.getPossibleConstraint();
1731 // if (constraintType != null) {
1732 // Item oppositeEnd = line.getOppositeEnd(this);
1733 // if (existingConstraint == null
1734 // || !existingConstraint.contains(oppositeEnd)) {
1735 // new Constraint(this, oppositeEnd,
1736 // getParentOrCurrentFrame().getNextItemID(),
1737 // constraintType);
1738 // }
1739 // }
1740 // }
1741 // }
1742 }
1743
1744 /**
1745 * Gets the parent frame if it is set or the current frame if this item does
1746 * not have a parent set.
1747 *
1748 * @return
1749 */
1750 public Frame getParentOrCurrentFrame() {
1751 // if the item is from an overlay the parent will NOT be null
1752 if (getParent() == null) {
1753 return DisplayIO.getCurrentFrame();
1754 }
1755 return getParent();
1756 }
1757
1758 /**
1759 * Sets the list of Dots (including this one) that form a closed shape.
1760 * Passing null sets this dot back to its normal (non-enclosed) state.
1761 *
1762 * @param enclosed
1763 * The List of Dots including this one that form a closed shape,
1764 * or null.
1765 */
1766 public void setEnclosedList(Collection<Item> enclosed) {
1767
1768 boolean changed = (_enclosure == null && enclosed != null);
1769
1770 if (_enclosure != null && enclosed == null) {
1771 invalidateFill();
1772 }
1773
1774 _enclosure = enclosed;
1775
1776 if (changed) {
1777 invalidateFill();
1778 ;
1779 }
1780 }
1781
1782 /**
1783 * Returns the polygon that represents the shape created by all the Dots in
1784 * this Dot's enclosed list. If the list is null, then null is returned.
1785 *
1786 * @return A Polygon the same shape and position as created by the Dots in
1787 * the enclosed list.
1788 */
1789 public Polygon getEnclosedShape() {
1790 if (_enclosure == null)
1791 return null;
1792
1793 Polygon poly = new Polygon();
1794 for (Item d : _enclosure) {
1795 poly.addPoint(d.getX(), d.getY());
1796 }
1797
1798 return poly;
1799 }
1800
1801 /**
1802 * Returns the list of Dots that, along with this Dot, form an enclosed
1803 * polygon. If this Dot is not part of an enclosure null may be returned.
1804 *
1805 * @return The List of Dots that form an enclosed shape with this Dot, or
1806 * null if this Dot is not part of an enclosure.
1807 */
1808 public Collection<Item> getEnclosingDots() {
1809 return _enclosure;
1810 }
1811
1812 /**
1813 * Returns whether this Dot has an assigned enclosure list of other Dots.
1814 * The result is the same as getEnclosedShape() != null.
1815 *
1816 * @return True if this Dot has an enclosure list of other Dots, false
1817 * otherwise.
1818 */
1819 public boolean isEnclosed() {
1820 return _enclosure != null;
1821 }
1822
1823 /**
1824 * True if this item is the end of a line.
1825 *
1826 * @return
1827 */
1828 public boolean isLineEnd() {
1829 // TODO this will need to be redone when enclosure class is added...
1830 // At the moment enclosures are only circles...we dont want circle
1831 // centers to be lineEnds
1832 return _lines.size() > 0;
1833 }
1834
1835 public boolean hasEnclosures() {
1836 return _enclosures.size() > 0;
1837 }
1838
1839 /**
1840 * Method that is called to notify an item that is on the end of a line that
1841 * its line has changed color.
1842 *
1843 * @param c
1844 * the new color for the line
1845 */
1846 protected void lineColorChanged(Color c) {
1847 for (Line l : getLines()) {
1848 if (l.getColor() != c)
1849 l.setColor(c);
1850 }
1851 }
1852
1853 /**
1854 * Checks if this item is off the left or top of the screen
1855 *
1856 * @return
1857 */
1858 public boolean offScreenTopOrLeft() {
1859 Rectangle itemRect = getArea().getBounds();
1860 // Check that the bottom right corner of this item is on the screen
1861 if (itemRect.x + itemRect.width >= 0
1862 && itemRect.y + itemRect.height >= 0)
1863 return false;
1864 // Check if all the items it is connected to are offscreen
1865 for (Item i : getAllConnected()) {
1866 Rectangle iRect = i.getArea().getBounds();
1867 // Check that the bottom right corner of this item is on the screen
1868 if (iRect.x + iRect.width >= 0 && iRect.y + iRect.height >= 0) {
1869 return false;
1870 }
1871 }
1872 return true;
1873 }
1874
1875 public void setConnectedToAnnotation(boolean val) {
1876 _connectedToAnnotation = val;
1877 }
1878
1879 public boolean isConnectedToAnnotation() {
1880 return _connectedToAnnotation;
1881 }
1882
1883 public void setData(String data) {
1884 if (data == null || data.length() == 0)
1885 _data = null;
1886 else {
1887 _data = new LinkedList<String>();
1888 _data.add(data);
1889 }
1890 }
1891
1892 public boolean hasAction() {
1893 return _actions != null && _actions.size() > 0;
1894 }
1895
1896 public void setAction(String action) {
1897 // Want to resize the highlight box for text items if actions are been
1898 // added
1899 if (action == null || action.length() == 0) {
1900 invalidateCommonTrait(ItemAppearence.LinkChanged);
1901 }
1902 if (_actions == null || _actions.size() == 0) {
1903 _poly = null;
1904 _actions = new LinkedList<String>();
1905 } else {
1906 _actions.clear();
1907 }
1908 if (action != null && action.length() > 0)
1909 _actions.add(action);
1910 invalidateCommonTrait(ItemAppearence.LinkChanged);
1911 }
1912
1913 protected int getLinkYOffset() {
1914 return 0;
1915 }
1916
1917 protected Rectangle getLinkDrawArea() { // TODO: Revise
1918 return new Rectangle(getX() - LEFT_MARGIN - 3, getY()
1919 + getLinkYOffset() - 3, 6, 6);
1920 }
1921
1922 /**
1923 * Paint the link symbol for the item if it is a
1924 *
1925 * @param g
1926 */
1927 protected void paintLink(Graphics2D g) {
1928 boolean hasLink = getLink() != null;
1929 boolean hasAction = hasAction();
1930
1931 if (hasLink || hasAction) {
1932 g.setStroke(HIGHLIGHT_STROKE);
1933 if (hasLink && hasAction) {
1934 g.setColor(LINK_ACTION_COLOR);
1935 } else if (hasLink) {
1936 g.setColor(LINK_COLOR);
1937 } else if (hasAction) {
1938 g.setColor(ACTION_COLOR);
1939 }
1940
1941 AffineTransform at = new AffineTransform();
1942 AffineTransform orig = g.getTransform();
1943 at.translate(getX() - LEFT_MARGIN, getY() + getLinkYOffset());
1944 g.setTransform(at);
1945
1946 if (getLinkMark() && getLink() != null) {
1947 g.drawPolygon(getLinkPoly());
1948
1949 // if the link is not valid, cross out the circle
1950 if (!isLinkValid())
1951 g.drawPolygon(getCircleCross());
1952 }
1953
1954 if (getActionMark() && hasAction()) {
1955 g.drawPolygon(getLinkPoly());
1956 g.fillPolygon(getLinkPoly());
1957
1958 // if the link is not valid, cross out the circle
1959 if (!isLinkValid() && getLink() != null) {
1960 g.setColor(getParent().getPaintBackgroundColor());
1961 g.drawPolygon(getCircleCross());
1962 }
1963 }
1964
1965 // reset the graphics tranformation
1966 g.setTransform(orig);
1967 }
1968 }
1969
1970 /**
1971 * Gets the distance between the start of the text and the left border of
1972 * the item. This distance changes depending on whether or not the item is
1973 * linked or has an associated action.
1974 *
1975 * @return the gap size in pixels
1976 */
1977 protected int getLeftMargin() {
1978 return ((getLinkMark() && getLink() != null)
1979 || (getActionMark() && getAction() != null) ? MARGIN_LEFT
1980 - MARGIN_RIGHT : MARGIN_RIGHT);
1981 }
1982
1983 public String getName() {
1984 return null;
1985 }
1986
1987 final public String getAbsoluteLinkTemplate() {
1988 return getAbsoluteLink(getLinkTemplate());
1989 }
1990
1991 final public String getAbsoluteLinkFrameset() {
1992 return getAbsoluteLink(getLinkFrameset());
1993 }
1994
1995 final public String getAbsoluteLink() {
1996 return getAbsoluteLink(getLink());
1997 }
1998
1999 /**
2000 * @param link
2001 * @return
2002 */
2003 private String getAbsoluteLink(String link) {
2004 if (link == null)
2005 return null;
2006 // assert (_parent!= null);
2007 Frame parent = getParentOrCurrentFrame();
2008 if (_parent == null) {
2009 // if parent is null it is an item on the message box
2010 // so it must already be absolute
2011 // assert (!FrameIO.isPositiveInteger(link));
2012 // return link;
2013
2014 }
2015
2016 // if its a relative link then return absolute
2017 if (FrameIO.isPositiveInteger(link)) {
2018 return parent.getFramesetName() + link;
2019 }
2020 return link;
2021 }
2022
2023 /**
2024 * Sets the x and y values of this item ignoring constraints.
2025 *
2026 * @param x
2027 * new x position
2028 * @param y
2029 * new y position
2030 */
2031 public void setXY(float x, float y) {
2032 _x = x;
2033 _y = y;
2034 }
2035
2036 /**
2037 * Recursive function for getting the path around a shape. This is used to
2038 * get the path that is painted on the screen.
2039 *
2040 * @param visited
2041 * @param points
2042 * @param addToEnd
2043 * @param toExplore
2044 */
2045 public void appendPath(Collection<Line> visited, LinkedList<Point> points,
2046 boolean addToEnd, Collection<Line> toExplore) {
2047
2048 if (addToEnd) {
2049 // put the start item points into our list
2050 points.addLast(getPosition());
2051 } else {
2052 points.addFirst(getPosition());
2053 }
2054
2055 // Find the line that has not been added yet
2056 LinkedList<Line> lines = new LinkedList<Line>();
2057 lines.addAll(getLines());
2058
2059 while (!lines.isEmpty()) {
2060 Line l = lines.remove();
2061 // if we havnt visited the line yet visit it
2062 if (!visited.contains(l)) {
2063 visited.add(l);
2064 Item otherEnd = l.getOppositeEnd(this);
2065 // Add all the enexplored lines to our list
2066 while (!lines.isEmpty()) {
2067 l = lines.remove();
2068 // Get the paths for the rest of the lines to be explored
2069 // later
2070 if (!toExplore.contains(l) && !visited.contains(l)) {
2071 toExplore.add(l);
2072 }
2073 }
2074 otherEnd.appendPath(visited, points, addToEnd, toExplore);
2075 }
2076 }
2077 }
2078
2079 /**
2080 * Gets the size of the enclosure that this item is part of. Used to
2081 * determine the paint order of items, with smaller items being painted
2082 * first.
2083 *
2084 * @return the area of the box surrounding the enclosed shape that this item
2085 * is part of
2086 */
2087 public double getEnclosedArea() {
2088 if (_enclosure == null)
2089 return 0.0;
2090 Rectangle2D box = getEnclosedShape().getBounds2D();
2091 return box.getWidth() * box.getHeight();
2092 }
2093
2094 public int getEnclosureID() {
2095 return _enclosure == null ? 0 : _enclosure.hashCode();
2096 }
2097
2098 /**
2099 * Returns the Shape that surrounds this Item representing this Item's
2100 * 'gravity'.
2101 *
2102 * @return The Shape (rectangle) surrounding this Item, which represents
2103 * this Items 'gravity'.
2104 */
2105 public final Polygon getPolygon() {
2106 if (_poly == null)
2107 updatePolygon();
2108
2109 return new Polygon(_poly.xpoints, _poly.ypoints, _poly.npoints);
2110 }
2111
2112 /**
2113 * Shifts the position of the item along the line between this items
2114 * location and a specified point.
2115 *
2116 * @param origin
2117 * @param ratio
2118 */
2119 public void translate(Point2D origin, double ratio) {
2120
2121 invalidateCommonTraitForAll(ItemAppearence.PreMoved);
2122
2123 _x = (float) (origin.getX() + ratio * (_x - origin.getX()));
2124 _y = (float) (origin.getY() + ratio * (_y - origin.getY()));
2125 updatePolygon();
2126 for (Line line : getLines())
2127 line.updatePolygon();
2128
2129 invalidateCommonTraitForAll(ItemAppearence.PostMoved);
2130
2131 }
2132
2133 private static int[] LinePatterns = new int[] { 0, 10, 20 };
2134
2135 /**
2136 * The rotates through a wheel of dashed lines.
2137 *
2138 * @param amount
2139 * number of rotations around the wheel to toggle by.
2140 */
2141 public void toggleDashed(int amount) {
2142 // find the index of the current line pattern
2143 int[] currentPattern = getLinePattern();
2144
2145 // Find the current pattern and move to the next pattern in the wheel
2146 for (int i = 0; i < LinePatterns.length; i++) {
2147 if (currentPattern == null || currentPattern[0] == LinePatterns[i]) {
2148 i += LinePatterns.length + amount;
2149 i %= LinePatterns.length;
2150
2151 // if we are at the start of the wheel make it 'null' (solid
2152 // line)
2153 if (i == 0) {
2154 setLinePattern(null);
2155 } else {
2156 setLinePattern(new int[] { LinePatterns[i], LinePatterns[i] });
2157 }
2158
2159 invalidateCommonTrait(ItemAppearence.ToggleDashed);
2160 return;
2161 }
2162 }
2163
2164 }
2165
2166 Collection<XRayable> _enclosures = new HashSet<XRayable>();
2167
2168 private boolean _deleted = false;
2169
2170 private Overlay _overlay = null;
2171
2172 /**
2173 * For now there can only be one enclosure per item
2174 *
2175 * @param enclosure
2176 */
2177 public void addEnclosure(XRayable enclosure) {
2178 _enclosures.clear();
2179 _enclosures.add(enclosure);
2180 }
2181
2182 public Collection<? extends XRayable> getEnclosures() {
2183 return _enclosures;
2184 }
2185
2186 public void removeEnclosure(Item i) {
2187 _enclosures.remove(i);
2188
2189 }
2190
2191 public boolean isDeleted() {
2192 return _deleted;
2193 }
2194
2195 /**
2196 * @return The full canvas that this item draws to. Must include
2197 * highlighting bounds
2198 */
2199 public Rectangle[] getDrawingArea() {
2200
2201 return new Rectangle[] { ItemUtils.expandRectangle(getPolygon()
2202 .getBounds(), _highlightThickness) };
2203
2204 }
2205
2206 /**
2207 *
2208 * @param area
2209 * @return True if area intersects with this items drawing area.
2210 */
2211 public final boolean isInDrawingArea(Area area) {
2212 for (Rectangle r : getDrawingArea()) {
2213 if (area.intersects(r))
2214 return true;
2215 }
2216 return false;
2217 }
2218
2219 /**
2220 * Completetly invalidates the item - so that it should be redrawed. Note:
2221 * This is handled internally, it should be reare to invoke this externally
2222 */
2223 public final void invalidateAll() {
2224 invalidate(getDrawingArea());
2225 }
2226
2227 /**
2228 * Invalidates areas on the parent frame. Purpose: to be called on specific
2229 * areas of the item that needs redrawing.
2230 *
2231 * @param damagedAreas
2232 */
2233 protected final void invalidate(Rectangle[] damagedAreas) {
2234 for (Rectangle r : damagedAreas)
2235 invalidate(r);
2236 }
2237
2238 /**
2239 * Invalidates areas on the parent frame. Purpose: to be called on specific
2240 * areas of the item that needs redrawing.
2241 *
2242 * @param damagedAreas
2243 */
2244 protected final void invalidate(Rectangle damagedArea) {
2245 FrameGraphics.invalidateItem(this, damagedArea);
2246 }
2247
2248 /**
2249 * Used to invalidate visual traits commonly shared by all items.
2250 *
2251 * @param trait
2252 */
2253 public final void invalidateCommonTrait(ItemAppearence trait) {
2254 invalidate(getDamagedArea(trait));
2255
2256 if (_colorFill != null
2257 && (trait == ItemAppearence.Added || trait == ItemAppearence.Removed)) {
2258 invalidateFill();
2259 }
2260 }
2261
2262 /**
2263 * Invalidates fill if has one, even if no color is set.
2264 */
2265 public void invalidateFill() {
2266 if (isLineEnd() && _enclosure != null) {
2267 invalidate(getEnclosedShape().getBounds());
2268 }
2269 }
2270
2271 /**
2272 * Default implementation always uses drawing area except for links, where
2273 * the link drawing area is used. Override to make item drawing more
2274 * efficient - defining only parts of the item that needs redrawing.
2275 *
2276 * @see Item.getDrawingArea
2277 *
2278 * @param trait
2279 * The visual trait that has changed.
2280 *
2281 * @return The damaged area according to the visual trait that has changed.
2282 */
2283 protected Rectangle[] getDamagedArea(ItemAppearence trait) {
2284
2285 if (trait == ItemAppearence.LinkChanged)
2286 return new Rectangle[] { getLinkDrawArea() }; // Invalidate area
2287 // where link is
2288 // drawn
2289
2290 return getDrawingArea();
2291
2292 }
2293
2294 public boolean hasVector() {
2295 return _overlay instanceof Vector;
2296 }
2297
2298 public boolean hasOverlay() {
2299 return _overlay != null;
2300 }
2301
2302 public Vector getVector() {
2303 if (_overlay instanceof Vector)
2304 return (Vector) _overlay;
2305 return null;
2306 }
2307
2308 public void setOverlay(Overlay overlay) {
2309 _overlay = overlay;
2310 }
2311
2312}
Note: See TracBrowser for help on using the repository browser.