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

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

Added Frame and Box variables to the Math calculations

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