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

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