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

Last change on this file since 121 was 121, checked in by bjn8, 16 years ago

Added invalidation for graphics... biiiig commit. LOts of effeciency improvements - now can animate

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