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

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

Widgets are now linkable.
Also made some improvements for popups - improved interaction with free items.

File size: 59.5 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 sourceFrame = getParentOrCurrentFrame();
1032 }
1033
1034 for (String s : getAction()) {
1035 Object returnValue = Actions.PerformActionCatchErrors(sourceFrame, sourceItem,
1036 s);
1037 if (returnValue != null) {
1038 FreeItems.getInstance().clear();
1039 if (returnValue instanceof Item) {
1040 Misc.attachToCursor((Item) returnValue);
1041 } else {
1042 Misc.attachStatsToCursor(returnValue.toString());
1043 }
1044 }
1045 }
1046 }
1047
1048 /**
1049 * Removes all constraints that this item has.
1050 *
1051 */
1052 public void removeAllConstraints() {
1053 while (_constraints.size() > 0) {
1054 Constraint c = _constraints.get(0);
1055 c.getEnd().removeConstraint(c);
1056 c.getStart().removeConstraint(c);
1057 }
1058 }
1059
1060 /**
1061 * Clears the list of Lines that this Dot is an end of. Note: This only
1062 * clears this Dot's list and does not have any affect on the Lines or other
1063 * Dots.
1064 */
1065 public void removeAllLines() {
1066 for (Line l : _lines) {
1067 l.invalidateAll();
1068 }
1069 _lines.clear();
1070 }
1071
1072 /**
1073 * Removes the given Constraint from the list of constraintss that this Dot
1074 * is a part of.
1075 *
1076 * @param c
1077 * The Constraint that this Dot is no longer a part of.
1078 */
1079 public void removeConstraint(Constraint c) {
1080 _constraints.remove(c);
1081 }
1082
1083 /**
1084 * Removes the given Line from the list of lines that this Dot is an end
1085 * for.
1086 *
1087 * @param line
1088 * The Line that this Dot is no longer an end of.
1089 */
1090 public void removeLine(Line line) {
1091 if (_lines.remove(line))
1092 line.invalidateAll();
1093 }
1094
1095 public void run() {
1096 try {
1097 Simple.ProgramStarted();
1098 Simple.RunFrameAndReportError(this, new Context());
1099 Simple.ProgramFinished();
1100 MessageBay.displayMessage(AgentStats.getStats(), GREEN);
1101 } catch (ConcurrentModificationException ce) {
1102 Simple.ProgramFinished();
1103 ce.printStackTrace();
1104 } catch (Exception e) {
1105 MessageBay.linkedErrorMessage(e.getMessage());
1106 Simple.ProgramFinished();
1107 }
1108 // Need to repaint any highlights etc
1109 FrameGraphics.requestRefresh(true);
1110 }
1111
1112 /**
1113 * Check if it has a relative link if so make it absolute.
1114 *
1115 */
1116 public void setAbsoluteLink() {
1117 String link = getLink();
1118 if (link == null)
1119 return;
1120 // Check if all the characters are digits and hence it is a relative
1121 // link
1122 if (!FrameIO.isPositiveInteger(link))
1123 return;
1124
1125 // Make it an absolute link
1126 String framesetName;
1127
1128 if (_parent == null)
1129 framesetName = DisplayIO.getCurrentFrame().getFramesetName();
1130 else
1131 framesetName = _parent.getFramesetName();
1132
1133 setLink(framesetName + link);
1134 }
1135
1136 /**
1137 * Sets any action code that should be associated with this Item Each entry
1138 * in the list is one line of code
1139 *
1140 * @param actions
1141 * The lines of code to associate with this Item
1142 */
1143 public void setActions(List<String> actions) {
1144 if (actions == null || actions.size() == 0) {
1145 invalidateCommonTrait(ItemAppearence.LinkChanged);
1146 _actions = null;
1147 } else
1148 _actions = new LinkedList<String>(actions);
1149
1150 // Want to resize the highlight box for text items if actions have been
1151 // added
1152 _poly = null;
1153 invalidateCommonTrait(ItemAppearence.LinkChanged);
1154 }
1155
1156 public void setData(List<String> data) {
1157 if (data == null || data.size() == 0)
1158 _data = null;
1159 else
1160 _data = new LinkedList<String>(data);
1161 }
1162
1163 public void setActionCursorEnter(List<String> enter) {
1164 _actionCursorEnter = enter;
1165 }
1166
1167 public void setActionCursorLeave(List<String> leave) {
1168 _actionCursorLeave = leave;
1169 }
1170
1171 public void setActionEnterFrame(List<String> enter) {
1172 _actionEnterFrame = enter;
1173 }
1174
1175 public void setActionLeaveFrame(List<String> leave) {
1176 _actionLeaveFrame = leave;
1177 }
1178
1179 public void setActionMark(boolean val) {
1180 if (!val)
1181 invalidateCommonTrait(ItemAppearence.LinkChanged);
1182 _poly = null;
1183 _actionMark = val;
1184 if (val)
1185 invalidateCommonTrait(ItemAppearence.LinkChanged);
1186 }
1187
1188 /**
1189 * Sets whether this Item is an Annotation.
1190 *
1191 * @param val
1192 * True if this Item is an Annotation, False otherwise.
1193 */
1194 public abstract void setAnnotation(boolean val);
1195
1196 /**
1197 * Used to set this Line as an Arrow. If length and ratio are 0, no arrow is
1198 * shown.
1199 *
1200 * @param length
1201 * The how far down the shaft of the line the arrowhead should
1202 * come.
1203 * @param ratio
1204 * The ratio of the arrow's length to its width.
1205 */
1206 public void setArrow(float length, double ratio) {
1207 _arrowheadLength = length;
1208 _arrowheadRatio = ratio;
1209 updateArrowPolygon();
1210 }
1211
1212 public void setArrowhead(Polygon arrow) {
1213 _arrowhead = arrow;
1214 }
1215
1216 public void setArrowheadLength(float length) {
1217 _arrowheadLength = length;
1218 updateArrowPolygon();
1219 }
1220
1221 public void setArrowheadRatio(double ratio) {
1222 _arrowheadRatio = ratio;
1223 updateArrowPolygon();
1224 }
1225
1226 public void setBackgroundColor(Color c) {
1227 if (c != _colorBackground) {
1228 _colorBackground = c;
1229 invalidateCommonTrait(ItemAppearence.BackgroundColorChanged);
1230 }
1231 }
1232
1233 /**
1234 * Sets the Color to use on the bottom and right sections of this Item's
1235 * border. If top is NULL, then the Item's background Color will be used.
1236 *
1237 * @param top
1238 * The Color to display in the bottom and right sections of this
1239 * Item's border.
1240 */
1241 public void setBottomShadowColor(Color bottom) {
1242 _colorBottomShadow = bottom;
1243 }
1244
1245 /**
1246 * Sets the foreground Color of this Item to the given Color.
1247 *
1248 * @param c
1249 */
1250 public void setColor(Color c) {
1251 if (c != _color) {
1252 _color = c;
1253 invalidateCommonTrait(ItemAppearence.ForegroundColorChanged);
1254 if (hasVector()) {
1255 // TODO make this more efficient so it only repaints the items
1256 // for this vector
1257 FrameKeyboardActions.Refresh();
1258 }
1259 }
1260 }
1261
1262 public void setConstraintIDs(String IDs) {
1263 }
1264
1265 public void setConstraints(List<Constraint> constraints) {
1266 _constraints = constraints;
1267 }
1268
1269 public void setTag(String newData) {
1270 if (newData != null)
1271 _tag = new StringBuffer(newData);
1272 else
1273 _tag = null;
1274 }
1275
1276 /**
1277 * Sets the created date of this Frame to the given String.
1278 *
1279 * @param date
1280 * The date to use for this Frame.
1281 */
1282 public void setDateCreated(String date) {
1283 _creationDate = date;
1284 }
1285
1286 public void setFillColor(Color c) {
1287
1288 _colorFill = c;
1289
1290 for (Line line : _lines) {
1291 Item other = line.getOppositeEnd(this);
1292 if (other.getFillColor() != c)
1293 other.setFillColor(c);
1294 }
1295
1296 invalidateCommonTrait(ItemAppearence.FillColor);
1297 invalidateFill();
1298 }
1299
1300 public void setGradientColor(Color c) {
1301 _colorGradient = c;
1302
1303 for (Line line : _lines) {
1304 Item other = line.getOppositeEnd(this);
1305 if (other.getGradientColor() != c)
1306 other.setGradientColor(c);
1307 }
1308
1309 invalidateCommonTrait(ItemAppearence.GradientColor);
1310 invalidateFill();
1311 }
1312
1313 public Color getGradientColor() {
1314 return _colorGradient;
1315 }
1316
1317 public void setFillPattern(String patternLink) {
1318 _fillPattern = patternLink;
1319 invalidateCommonTrait(ItemAppearence.FillPattern);
1320 invalidateFill();
1321 }
1322
1323 public void setFloating(boolean val) {
1324 _floating = val;
1325 }
1326
1327 public void setHighlight(boolean val) {
1328 _highlight = val;
1329 }
1330
1331 /**
1332 * Sets the ID of this Item to the given Integer. Note: Items with ID's < 0
1333 * are not saved
1334 *
1335 * @param newID
1336 * The new ID to assign this Item.
1337 */
1338 public void setID(int newID) {
1339 _id = newID;
1340 }
1341
1342 /**
1343 * Sets the list of lines that this point is part of (may be set to null).
1344 *
1345 * @param lineID
1346 * A String of line ID numbers separated by spaces.
1347 */
1348 public void setLineIDs(String lineID) {
1349 }
1350
1351 public void setLinePattern(int[] pattern) {
1352 _linePattern = pattern;
1353
1354 for (Line line : getLines())
1355 line.setLinePattern(pattern);
1356 }
1357
1358 public void setLines(List<Line> lines) {
1359 _lines = lines;
1360
1361 for (Line line : lines)
1362 line.setLinePattern(getLinePattern());
1363
1364 }
1365
1366 /**
1367 * Links this item to the given Frame, this may be set to null to remove a
1368 * link.
1369 *
1370 * @param frameName
1371 * The name of the Frame to link this item to.
1372 */
1373 public void setLink(String frameName) {
1374 if (frameName == null) {
1375 invalidateCommonTrait(ItemAppearence.LinkChanged);
1376 }
1377
1378 // If a link is being removed or set then need to reset poly so the
1379 // highlighting is drawn with the correct width
1380 if (frameName == null || getLink() == null)
1381 _poly = null;
1382
1383 if (FrameIO.isValidLink(frameName))
1384 _link = frameName;
1385 else
1386 MessageBay.errorMessage("[" + frameName
1387 + "] is not a valid frame name");
1388 // TODO make this throw exceptions etc...
1389
1390 invalidateCommonTrait(ItemAppearence.LinkChanged);
1391 }
1392
1393 public void setLinkFrameset(String frameset) {
1394 if (frameset == null || FrameIO.isValidFramesetName(frameset))
1395 _link_frameset = frameset;
1396 else
1397 MessageBay.errorMessage("[" + frameset
1398 + "] is not a valid frameset name");
1399 // TODO make this throw exceptions etc...
1400 }
1401
1402 public void setLinkMark(boolean val) {
1403 if (!val)
1404 invalidateCommonTrait(ItemAppearence.LinkChanged);
1405 _poly = null;
1406 _linkMark = val;
1407 if (val)
1408 invalidateCommonTrait(ItemAppearence.LinkChanged);
1409 }
1410
1411 public void setLinkTemplate(String template) {
1412 if (FrameIO.isValidLink(template))
1413 _link_template = template;
1414 else
1415 MessageBay.errorMessage("[" + template
1416 + "] is not a valid frame name");
1417 // TODO make this throw exceptions etc...
1418 }
1419
1420 // /**
1421 // * Sets the maximum coordinates on the screen that this item may occupy.
1422 // * This is used by Text items to compute word-wrapping lengths.
1423 // *
1424 // * @param d
1425 // * The Maximum size of the Frame containing this Item.
1426 // */
1427 // public void setMaxWidth(int width) {
1428 // if (width > 0) {
1429 // _maxWidth = width;
1430 // updatePolygon();
1431 // }
1432 // }
1433
1434 public void setOffset(int x, int y) {
1435 _offset.setLocation(x, y);
1436 }
1437
1438 public void setOffset(Point p) {
1439 _offset.setLocation(p);
1440 }
1441
1442 public void setOwner(String own) {
1443 _owner = own;
1444 }
1445
1446 public void setParent(Frame frame) {
1447 _parent = frame;
1448 }
1449
1450 /**
1451 * Invalidates this, connected lines and fill
1452 *
1453 * @param trait
1454 */
1455 private void invalidateCommonTraitForAll(ItemAppearence trait) {
1456 invalidateCommonTrait(trait);
1457 for (Line line : getLines())
1458 line.invalidateCommonTrait(trait);
1459 if (_colorFill != null) {
1460 invalidateFill(); // only invalidates if has fill
1461 }
1462 for (XRayable x : getEnclosures()) {
1463 x.invalidateCommonTrait(trait);
1464 }
1465
1466 }
1467
1468 /**
1469 * Sets the position of this item on the screen
1470 *
1471 * @param x
1472 * The new X coordinate
1473 * @param y
1474 * The new Y coordinate
1475 */
1476 public void setPosition(float x, float y) {
1477 float deltaX = x - _x;
1478 float deltaY = y - _y;
1479
1480 if (deltaX == 0 && deltaY == 0)
1481 return;
1482
1483 invalidateCommonTraitForAll(ItemAppearence.PreMoved);
1484
1485 _x = x;
1486 _y = y;
1487
1488 for (Item i : getEnclosures()) {
1489 i.updatePolygon();
1490 }
1491 updatePolygon();
1492
1493 // update the position of any dots that are constrained by this one
1494 for (Constraint c : _constraints) {
1495 Item other = c.getOppositeEnd(this);
1496
1497 // only set position if the other dot is still fixed to the
1498 // frame
1499 if (/* this.isFloating() && */!other.isFloating()) {
1500 if (c.getType() == Constraint.HORIZONTAL) {
1501 if (other._y != y) {
1502 other.setY(y);
1503 }
1504 } else if (c.getType() == Constraint.VERTICAL) {
1505 if (other._x != x) {
1506 other.setX(x);
1507 }
1508 } else if (c.isDiagonal()) {
1509 if (Math.abs(other._x - x) != Math.abs(other._y - y)) {
1510
1511 float m1 = c.getGradient();
1512 float c1 = y - m1 * x;
1513 // Now work out the equation for the second line
1514 // Get the first line the other end is attached to that
1515 // is not the diagonal line
1516 List<Line> lines = other.getLines();
1517 // If there is only one line...
1518 if (lines.size() == 1) {
1519 if (m1 != 0) {
1520 if (Math.abs(deltaX) > Math.abs(deltaY)) {
1521 other.setX((other._y - c1) / m1);
1522 } else {
1523 other.setY(m1 * other._x + c1);
1524 }
1525 }
1526 } else if (lines.size() > 1) {
1527 Line otherLine = lines.get(0);
1528 Item end = otherLine.getOppositeEnd(other);
1529 if (end.equals(this)) {
1530 otherLine = lines.get(1);
1531 end = otherLine.getOppositeEnd(other);
1532 assert (!end.equals(this));
1533 }
1534
1535 float xDiff = end._x - other._x;
1536 float yDiff = end._y - other._y;
1537 if (xDiff == 0) {
1538 other.setY(m1 * other._x + c1);
1539 } else if (Math.abs(xDiff) == Math.abs(yDiff)
1540 && !this.isFloating() && deltaX == 0
1541 && deltaY == 0) {
1542 if (deltaX == 0) {
1543 _x = (_y - other._y) * m1 + other._x;
1544 } else {
1545 _y = (_x - other._x) * m1 + other._y;
1546 }
1547 } else {
1548 float m2 = yDiff / xDiff;
1549 float c2 = end._y - m2 * end._x;
1550 float mDiff = m1 - m2;
1551 if (Math.abs(mDiff) < 0.000001) {
1552 assert (false);
1553 // TODO how do I handle this case!!
1554 } else {
1555 float newX = (c2 - c1) / mDiff;
1556 float newY = m1 * newX + c1;
1557 if (other._x != newX
1558 /* && other._y != newY */) {
1559 other.setPosition(newX, newY);
1560 }
1561 }
1562 }
1563 }
1564 // Do simultaneous equations to get the new postion for
1565 // the other end of the diagonal line
1566 }
1567 }
1568 }
1569 }
1570
1571 for (Line line : getLines()) {
1572 line.updatePolygon();
1573 }
1574
1575 invalidateCommonTraitForAll(ItemAppearence.PostMoved);
1576
1577 }
1578
1579 public void setPosition(Point position) {
1580 setPosition(position.x, position.y);
1581 }
1582
1583 public void setRelativeLink() {
1584 String link = getLink();
1585 if (link == null)
1586 return;
1587 assert (_parent != null);
1588
1589 if (FrameIO.isPositiveInteger(link))
1590 return;
1591
1592 // Check if the link is for the current frameset
1593 if (_parent.getFramesetName().equalsIgnoreCase(
1594 Conversion.getFramesetName(link))) {
1595 setLink("" + Conversion.getFrameNumber(link));
1596 }
1597 }
1598
1599 /**
1600 * Sets the size of this Item. For Text this is the Font size. For Lines and
1601 * Dots this is the thickness.
1602 */
1603 public void setSize(float size) {
1604 }
1605
1606 /**
1607 * Sets the thickness of the item.
1608 *
1609 * @param thick
1610 */
1611 public void setThickness(float thick) {
1612 setThickness(thick, true);
1613 }
1614
1615 /**
1616 * Sets the thickness of this item.
1617 *
1618 * @param thick
1619 * the new thickness for the item
1620 * @param setConnectedThickness
1621 * true if all items connected to this item should also have
1622 * their thickness set
1623 */
1624 public void setThickness(float thick, boolean setConnectedThickness) {
1625 if (thick == _thickness)
1626 return;
1627 boolean bigger = thick > _thickness;
1628
1629 if (!bigger) {
1630 if (setConnectedThickness) {
1631 // TODO is there a more efficient way of doing this?
1632 for (Item i : getConnected())
1633 i.invalidateCommonTrait(ItemAppearence.Thickness);
1634 } else {
1635 invalidateCommonTrait(ItemAppearence.Thickness);
1636 }
1637 }
1638
1639 _thickness = thick;
1640 // update the size of any lines
1641 for (Line line : getLines())
1642 line.setThickness(thick, setConnectedThickness);
1643
1644 updatePolygon();
1645
1646 if (bigger) {
1647 if (setConnectedThickness) {
1648 for (Item i : getConnected())
1649 i.invalidateCommonTrait(ItemAppearence.Thickness);
1650 } else {
1651 invalidateCommonTrait(ItemAppearence.Thickness);
1652 }
1653 }
1654 }
1655
1656 /**
1657 * Returns the thickness (in pixels) of this Dot.
1658 *
1659 * @return The 'thickness' of this Dot. (returns -1 if the thickness is not
1660 * set).
1661 */
1662 public float getThickness() {
1663 return _thickness;
1664 }
1665
1666 /**
1667 * Sets the Color to use on the top and left sections of this Item's border.
1668 * If top is NULL, then the Item's background Color will be used.
1669 *
1670 * @param top
1671 * The Color to display in the top and left sections of this
1672 * Item's border.
1673 */
1674 public void setTopShadowColor(Color top) {
1675 _colorTopShadow = top;
1676 }
1677
1678 public void setWidth(int width) throws UnsupportedOperationException {
1679 throw new UnsupportedOperationException(
1680 "Item type does not support width attribute!");
1681 }
1682
1683 /**
1684 * Sets the position of this Item on the X axis
1685 *
1686 * @param newX
1687 * The position on the X axis to assign to this Item
1688 */
1689 public void setX(float newX) {
1690 setPosition(newX, getY());
1691 }
1692
1693 /**
1694 * Sets the position of this Item on the Y axis
1695 *
1696 * @param newY
1697 * The position on the Y axis to assign to this Item
1698 */
1699 public void setY(float newY) {
1700 setPosition(getX(), newY);
1701 }
1702
1703 /**
1704 * Paints any highlighting of this Item. This may include changing the
1705 * thickness (lines) or painting a box around the item (Text, Images). If
1706 * val is True then the Graphics Color is changed to the highlight Color, if
1707 * False then the Graphics Color is left unchanged (for clearing of
1708 * highlighting).
1709 *
1710 * @param val
1711 * True if this Item should be highlighted, false if the
1712 * highlighting is being cleared.
1713 * @return The desired mouse cursor when this Item is highlighted (negative
1714 * means no change)
1715 */
1716 public int setHighlightColor() {
1717 return setHighlightColor(DEFAULT_HIGHLIGHT);
1718 }
1719
1720 public int setHighlightColor(Color c) {
1721
1722 _highlightThickness = DEFAULT_HIGHLIGHT_THICKNESS;
1723
1724 Color selColor = (c != null) ? c : DEFAULT_HIGHLIGHT;
1725 if (_highlightColor != c) {
1726 _highlightColor = selColor;
1727 this.invalidateCommonTrait(ItemAppearence.HighlightColorChanged);
1728 }
1729
1730 return Item.UNCHANGED_CURSOR;
1731
1732 }
1733
1734 private void updateArrowPolygon() {
1735 if (getArrowheadLength() < 0 || getArrowheadRatio() < 0)
1736 _arrowhead = null;
1737 else {
1738 _arrowhead = new Polygon();
1739 _arrowhead.addPoint(Math.round(getX()), Math.round(getY()));
1740 _arrowhead.addPoint((Math.round(getX() - getArrowheadLength())),
1741 ((int) Math.round(getY()
1742 - (getArrowheadLength() * getArrowheadRatio()))));
1743 _arrowhead.addPoint(Math.round(getX()), (int) getY());
1744 _arrowhead
1745 .addPoint(
1746 (int) Math.round(getX() - getArrowheadLength()),
1747 (int) Math
1748 .round((getY() + (getArrowheadLength() * getArrowheadRatio()))));
1749 }
1750 }
1751
1752 public abstract void updatePolygon();
1753
1754 public void setHidden(boolean state) {
1755 this._visible = !state;
1756 }
1757
1758 public void setVisible(boolean state) {
1759 this._visible = state;
1760 }
1761
1762 public boolean isVisible() {
1763 return _visible && !_deleted;
1764 }
1765
1766 /**
1767 * Raised whenever the item is removed, added, no longer in view (That is,
1768 * when it is not on any of the current frames, of overlays of the current
1769 * frames) or has become visible. That is, when it is either on a current
1770 * frame, or an overlay of a current frame.
1771 *
1772 * @param e
1773 * The event
1774 */
1775 public void onParentStateChanged(ItemParentStateChangedEvent e) {
1776 }
1777
1778 public void setHighlightMode(HighlightMode mode, Color color) {
1779 setHighlightColor(color);
1780 if (hasPermission(Permission.followLinks)) {
1781 if (_mode != mode) {
1782 _mode = mode;
1783 this.invalidateCommonTrait(ItemAppearence.HighlightModeChanged);
1784 }
1785 }
1786 }
1787
1788 public HighlightMode getHighlightMode() {
1789 return _mode;
1790 }
1791
1792 public void anchor() {
1793 Frame current = getParentOrCurrentFrame();
1794 setID(current.getNextItemID());
1795 setOffset(0, 0);
1796 setParent(current);
1797
1798 current.addItem(this);
1799 current.setResort(true);
1800 setRelativeLink();
1801 setFloating(false);
1802
1803 // // If its an unconstrained line end check if we should add a
1804 // constraint
1805 // if (isLineEnd() && getLines().size() <= 2
1806 // && getConstraints().size() <= 1) {
1807 // Constraint existingConstraint = null;
1808 // List<Constraint> constraints = getConstraints();
1809 // // Get the existing constraint
1810 // if (constraints.size() > 0) {
1811 // existingConstraint = constraints.get(0);
1812 // }
1813 // for (Line line : getLines()) {
1814 // Integer constraintType = line.getPossibleConstraint();
1815 // if (constraintType != null) {
1816 // Item oppositeEnd = line.getOppositeEnd(this);
1817 // if (existingConstraint == null
1818 // || !existingConstraint.contains(oppositeEnd)) {
1819 // new Constraint(this, oppositeEnd,
1820 // getParentOrCurrentFrame().getNextItemID(),
1821 // constraintType);
1822 // }
1823 // }
1824 // }
1825 // }
1826 }
1827
1828 /**
1829 * Gets the parent frame if it is set or the current frame if this item does
1830 * not have a parent set.
1831 *
1832 * @return
1833 */
1834 public Frame getParentOrCurrentFrame() {
1835 // if the item is from an overlay the parent will NOT be null
1836 if (getParent() == null) {
1837 return DisplayIO.getCurrentFrame();
1838 }
1839 return getParent();
1840 }
1841
1842 /**
1843 * Sets the list of Dots (including this one) that form a closed shape.
1844 * Passing null sets this dot back to its normal (non-enclosed) state.
1845 *
1846 * @param enclosed
1847 * The List of Dots including this one that form a closed shape,
1848 * or null.
1849 */
1850 public void setEnclosedList(Collection<Item> enclosed) {
1851
1852 boolean changed = (_enclosure == null && enclosed != null);
1853
1854 if (_enclosure != null && enclosed == null) {
1855 invalidateFill();
1856 }
1857
1858 _enclosure = enclosed;
1859
1860 if (changed) {
1861 invalidateFill();
1862 ;
1863 }
1864 }
1865
1866 /**
1867 * Returns the polygon that represents the shape created by all the Dots in
1868 * this Dot's enclosed list. If the list is null, then null is returned.
1869 *
1870 * @return A Polygon the same shape and position as created by the Dots in
1871 * the enclosed list.
1872 */
1873 public Polygon getEnclosedShape() {
1874 if (_enclosure == null)
1875 return null;
1876
1877 Polygon poly = new Polygon();
1878 for (Item d : _enclosure) {
1879 poly.addPoint(d.getX(), d.getY());
1880 }
1881
1882 return poly;
1883 }
1884
1885 /**
1886 * Returns the list of Dots that, along with this Dot, form an enclosed
1887 * polygon. If this Dot is not part of an enclosure null may be returned.
1888 *
1889 * @return The List of Dots that form an enclosed shape with this Dot, or
1890 * null if this Dot is not part of an enclosure.
1891 */
1892 public Collection<Item> getEnclosingDots() {
1893 return _enclosure;
1894 }
1895
1896 /**
1897 * Returns whether this Dot has an assigned enclosure list of other Dots.
1898 * The result is the same as getEnclosedShape() != null.
1899 *
1900 * @return True if this Dot has an enclosure list of other Dots, false
1901 * otherwise.
1902 */
1903 public boolean isEnclosed() {
1904 return _enclosure != null;
1905 }
1906
1907 /**
1908 * True if this item is the end of a line.
1909 *
1910 * @return
1911 */
1912 public boolean isLineEnd() {
1913 // TODO this will need to be redone when enclosure class is added...
1914 // At the moment enclosures are only circles...we dont want circle
1915 // centers to be lineEnds
1916 return _lines.size() > 0;
1917 }
1918
1919 public boolean hasEnclosures() {
1920 return _enclosures.size() > 0;
1921 }
1922
1923 /**
1924 * Method that is called to notify an item that is on the end of a line that
1925 * its line has changed color.
1926 *
1927 * @param c
1928 * the new color for the line
1929 */
1930 protected void lineColorChanged(Color c) {
1931 for (Line l : getLines()) {
1932 if (l.getColor() != c)
1933 l.setColor(c);
1934 }
1935 }
1936
1937 /**
1938 * Checks if this item is off the left or top of the screen
1939 *
1940 * @return
1941 */
1942 public boolean offScreenTopOrLeft() {
1943 Rectangle itemRect = getArea().getBounds();
1944 // Check that the bottom right corner of this item is on the screen
1945 if (itemRect.x + itemRect.width >= 0
1946 && itemRect.y + itemRect.height >= 0)
1947 return false;
1948 // Check if all the items it is connected to are offscreen
1949 for (Item i : getAllConnected()) {
1950 Rectangle iRect = i.getArea().getBounds();
1951 // Check that the bottom right corner of this item is on the screen
1952 if (iRect.x + iRect.width >= 0 && iRect.y + iRect.height >= 0) {
1953 return false;
1954 }
1955 }
1956 return true;
1957 }
1958
1959 public void setConnectedToAnnotation(boolean val) {
1960 _connectedToAnnotation = val;
1961 }
1962
1963 public boolean isConnectedToAnnotation() {
1964 return _connectedToAnnotation;
1965 }
1966
1967 public void setData(String data) {
1968 if (data == null || data.length() == 0)
1969 _data = null;
1970 else {
1971 _data = new LinkedList<String>();
1972 _data.add(data);
1973 }
1974 }
1975
1976 public boolean hasAction() {
1977 List<String> actions = getAction();
1978 return actions != null && actions.size() > 0;
1979 }
1980
1981 public void setAction(String action) {
1982 // Want to resize the highlight box for text items if actions are been
1983 // added
1984 if (action == null || action.length() == 0) {
1985 invalidateCommonTrait(ItemAppearence.LinkChanged);
1986 }
1987 if (_actions == null || _actions.size() == 0) {
1988 _poly = null;
1989 _actions = new LinkedList<String>();
1990 } else {
1991 _actions.clear();
1992 }
1993 if (action != null && action.length() > 0)
1994 _actions.add(action);
1995 invalidateCommonTrait(ItemAppearence.LinkChanged);
1996 }
1997
1998 protected int getLinkYOffset() {
1999 return 0;
2000 }
2001
2002 protected Rectangle getLinkDrawArea() {
2003 return getLinkDrawArea(getX() - LEFT_MARGIN, getY() + getLinkYOffset());
2004 }
2005
2006 /**
2007 * TODO: Revise - it would be good to have a member that
2008 * defines the link dimensions.
2009 *
2010 * @param x
2011 * Left of graphic (i.e not centered)
2012 * @param y
2013 * Above graphic (i.e not centered)
2014 *
2015 * @return
2016 * The drawing area of the link at the given coordinates.
2017 */
2018 Rectangle getLinkDrawArea(int x, int y) {
2019 return new Rectangle(x + 2, y - 1, 8, 8);
2020 }
2021
2022 /**
2023 * Paint the link symbol for the item if it is a
2024 *
2025 * @param g
2026 */
2027 protected void paintLink(Graphics2D g) {
2028 paintLinkGraphic(g, getX() - LEFT_MARGIN, getY() + getLinkYOffset());
2029 }
2030
2031 /**
2032 * Paint the link symbol for the item at a given position.
2033 *
2034 * @see #paintLink
2035 *
2036 * @param g
2037 * The graphics to paint with
2038 *
2039 * @param x
2040 * The x position of the link. Left of graphic (i.e not centered)
2041 *
2042 * @param y
2043 * The y position of the link. Above of graphic (i.e not centered)
2044 */
2045 void paintLinkGraphic(Graphics2D g, int x, int y) {
2046
2047 boolean hasLink = getLink() != null;
2048 boolean hasAction = hasAction();
2049
2050 if (hasLink || hasAction) {
2051 g.setStroke(HIGHLIGHT_STROKE);
2052 if (hasLink && hasAction) {
2053 g.setColor(LINK_ACTION_COLOR);
2054 } else if (hasLink) {
2055 g.setColor(LINK_COLOR);
2056 } else if (hasAction) {
2057 g.setColor(ACTION_COLOR);
2058 }
2059
2060 AffineTransform at = new AffineTransform();
2061 AffineTransform orig = g.getTransform();
2062 at.translate(x, y);
2063 g.setTransform(at);
2064
2065 if (getLinkMark() && getLink() != null) {
2066 g.drawPolygon(getLinkPoly());
2067
2068 // if the link is not valid, cross out the circle
2069 if (!isLinkValid())
2070 g.drawPolygon(getCircleCross());
2071 }
2072
2073 if (getActionMark() && hasAction()) {
2074 g.drawPolygon(getLinkPoly());
2075 g.fillPolygon(getLinkPoly());
2076
2077 // if the link is not valid, cross out the circle
2078 if (!isLinkValid() && getLink() != null) {
2079 g.setColor(getParent().getPaintBackgroundColor());
2080 g.drawPolygon(getCircleCross());
2081 }
2082 }
2083
2084 // reset the graphics tranformation
2085 g.setTransform(orig);
2086 }
2087 }
2088
2089
2090
2091 /**
2092 * Gets the distance between the start of the text and the left border of
2093 * the item. This distance changes depending on whether or not the item is
2094 * linked or has an associated action.
2095 *
2096 * @return the gap size in pixels
2097 */
2098 protected int getLeftMargin() {
2099 return ((getLinkMark() && getLink() != null)
2100 || (getActionMark() && getAction() != null) ? MARGIN_LEFT
2101 - MARGIN_RIGHT : MARGIN_RIGHT);
2102 }
2103
2104 public String getName() {
2105 return null;
2106 }
2107
2108 final public String getAbsoluteLinkTemplate() {
2109 return getAbsoluteLink(getLinkTemplate());
2110 }
2111
2112 final public String getAbsoluteLinkFrameset() {
2113 return getAbsoluteLink(getLinkFrameset());
2114 }
2115
2116 final public String getAbsoluteLink() {
2117 return getAbsoluteLink(getLink());
2118 }
2119
2120 /**
2121 * @param link
2122 * @return
2123 */
2124 private String getAbsoluteLink(String link) {
2125 if (link == null)
2126 return null;
2127 // assert (_parent!= null);
2128 Frame parent = getParentOrCurrentFrame();
2129 if (_parent == null) {
2130 // if parent is null it is an item on the message box
2131 // so it must already be absolute
2132 // assert (!FrameIO.isPositiveInteger(link));
2133 // return link;
2134
2135 }
2136
2137 // if its a relative link then return absolute
2138 if (FrameIO.isPositiveInteger(link)) {
2139 return parent.getFramesetName() + link;
2140 }
2141 return link;
2142 }
2143
2144 /**
2145 * Sets the x and y values of this item ignoring constraints.
2146 *
2147 * @param x
2148 * new x position
2149 * @param y
2150 * new y position
2151 */
2152 public void setXY(float x, float y) {
2153 _x = x;
2154 _y = y;
2155 }
2156
2157 /**
2158 * Recursive function for getting the path around a shape. This is used to
2159 * get the path that is painted on the screen.
2160 *
2161 * @param visited
2162 * @param points
2163 * @param addToEnd
2164 * @param toExplore
2165 */
2166 public void appendPath(Collection<Line> visited, LinkedList<Point> points,
2167 boolean addToEnd, Collection<Line> toExplore) {
2168
2169 if (addToEnd) {
2170 // put the start item points into our list
2171 points.addLast(getPosition());
2172 } else {
2173 points.addFirst(getPosition());
2174 }
2175
2176 // Find the line that has not been added yet
2177 LinkedList<Line> lines = new LinkedList<Line>();
2178 lines.addAll(getLines());
2179
2180 while (!lines.isEmpty()) {
2181 Line l = lines.remove();
2182 // if we havnt visited the line yet visit it
2183 if (!visited.contains(l)) {
2184 visited.add(l);
2185 Item otherEnd = l.getOppositeEnd(this);
2186 // Add all the enexplored lines to our list
2187 while (!lines.isEmpty()) {
2188 l = lines.remove();
2189 // Get the paths for the rest of the lines to be explored
2190 // later
2191 if (!toExplore.contains(l) && !visited.contains(l)) {
2192 toExplore.add(l);
2193 }
2194 }
2195 otherEnd.appendPath(visited, points, addToEnd, toExplore);
2196 }
2197 }
2198 }
2199
2200 /**
2201 * Gets the size of the enclosure that this item is part of. Used to
2202 * determine the paint order of items, with smaller items being painted
2203 * first.
2204 *
2205 * @return the area of the box surrounding the enclosed shape that this item
2206 * is part of
2207 */
2208 public double getEnclosedArea() {
2209 if (_enclosure == null)
2210 return 0.0;
2211 Rectangle2D box = getEnclosedShape().getBounds2D();
2212 return box.getWidth() * box.getHeight();
2213 }
2214
2215 public int getEnclosureID() {
2216 return _enclosure == null ? 0 : _enclosure.hashCode();
2217 }
2218
2219 /**
2220 * Returns the Shape that surrounds this Item representing this Item's
2221 * 'gravity'.
2222 *
2223 * @return The Shape (rectangle) surrounding this Item, which represents
2224 * this Items 'gravity'.
2225 */
2226 public final Polygon getPolygon() {
2227 if (_poly == null)
2228 updatePolygon();
2229
2230 return new Polygon(_poly.xpoints, _poly.ypoints, _poly.npoints);
2231 }
2232
2233 /**
2234 * Shifts the position of the item along the line between this items
2235 * location and a specified point.
2236 *
2237 * @param origin
2238 * @param ratio
2239 */
2240 public void translate(Point2D origin, double ratio) {
2241
2242 invalidateCommonTraitForAll(ItemAppearence.PreMoved);
2243
2244 _x = (float) (origin.getX() + ratio * (_x - origin.getX()));
2245 _y = (float) (origin.getY() + ratio * (_y - origin.getY()));
2246 updatePolygon();
2247 for (Line line : getLines())
2248 line.updatePolygon();
2249
2250 invalidateCommonTraitForAll(ItemAppearence.PostMoved);
2251
2252 }
2253
2254 private static int[] LinePatterns = new int[] { 0, 10, 20 };
2255
2256 /**
2257 * The rotates through a wheel of dashed lines.
2258 *
2259 * @param amount
2260 * number of rotations around the wheel to toggle by.
2261 */
2262 public void toggleDashed(int amount) {
2263 // find the index of the current line pattern
2264 int[] currentPattern = getLinePattern();
2265
2266 // Find the current pattern and move to the next pattern in the wheel
2267 for (int i = 0; i < LinePatterns.length; i++) {
2268 if (currentPattern == null || currentPattern[0] == LinePatterns[i]) {
2269 i += LinePatterns.length + amount;
2270 i %= LinePatterns.length;
2271
2272 // if we are at the start of the wheel make it 'null' (solid
2273 // line)
2274 if (i == 0) {
2275 setLinePattern(null);
2276 } else {
2277 setLinePattern(new int[] { LinePatterns[i], LinePatterns[i] });
2278 }
2279
2280 invalidateCommonTrait(ItemAppearence.ToggleDashed);
2281 return;
2282 }
2283 }
2284
2285 }
2286
2287 Collection<XRayable> _enclosures = new HashSet<XRayable>();
2288
2289 private boolean _deleted = false;
2290
2291 private Overlay _overlay = null;
2292
2293 /**
2294 * For now there can only be one enclosure per item
2295 *
2296 * @param enclosure
2297 */
2298 public void addEnclosure(XRayable enclosure) {
2299 _enclosures.clear();
2300 _enclosures.add(enclosure);
2301 }
2302
2303 public Collection<? extends XRayable> getEnclosures() {
2304 return _enclosures;
2305 }
2306
2307 public void removeEnclosure(Item i) {
2308 _enclosures.remove(i);
2309
2310 }
2311
2312 public boolean isDeleted() {
2313 return _deleted;
2314 }
2315
2316 /**
2317 * @return The full canvas that this item draws to. Must include
2318 * highlighting bounds
2319 */
2320 public Rectangle[] getDrawingArea() {
2321
2322 return new Rectangle[] { ItemUtils.expandRectangle(getPolygon()
2323 .getBounds(), _highlightThickness) };
2324
2325 }
2326
2327 /**
2328 *
2329 * @param area
2330 * @return True if area intersects with this items drawing area.
2331 */
2332 public final boolean isInDrawingArea(Area area) {
2333 for (Rectangle r : getDrawingArea()) {
2334 if (area.intersects(r))
2335 return true;
2336 }
2337 return false;
2338 }
2339
2340 /**
2341 * Completetly invalidates the item - so that it should be redrawed. Note:
2342 * This is handled internally, it should be reare to invoke this externally
2343 */
2344 public final void invalidateAll() {
2345 invalidate(getDrawingArea());
2346 }
2347
2348 /**
2349 * Invalidates areas on the parent frame. Purpose: to be called on specific
2350 * areas of the item that needs redrawing.
2351 *
2352 * @param damagedAreas
2353 */
2354 protected final void invalidate(Rectangle[] damagedAreas) {
2355 for (Rectangle r : damagedAreas)
2356 invalidate(r);
2357 }
2358
2359 /**
2360 * Invalidates areas on the parent frame. Purpose: to be called on specific
2361 * areas of the item that needs redrawing.
2362 *
2363 * @param damagedAreas
2364 */
2365 protected final void invalidate(Rectangle damagedArea) {
2366 FrameGraphics.invalidateItem(this, damagedArea);
2367 }
2368
2369 /**
2370 * Used to invalidate visual traits commonly shared by all items.
2371 *
2372 * @param trait
2373 */
2374 public final void invalidateCommonTrait(ItemAppearence trait) {
2375 invalidate(getDamagedArea(trait));
2376
2377 if (_colorFill != null
2378 && (trait == ItemAppearence.Added || trait == ItemAppearence.Removed)) {
2379 invalidateFill();
2380 }
2381 }
2382
2383 /**
2384 * Invalidates fill if has one, even if no color is set.
2385 */
2386 public void invalidateFill() {
2387 if (isLineEnd() && _enclosure != null) {
2388 invalidate(getEnclosedShape().getBounds());
2389 }
2390 }
2391
2392 /**
2393 * Default implementation always uses drawing area except for links, where
2394 * the link drawing area is used. Override to make item drawing more
2395 * efficient - defining only parts of the item that needs redrawing.
2396 *
2397 * @see Item.getDrawingArea
2398 *
2399 * @param trait
2400 * The visual trait that has changed.
2401 *
2402 * @return The damaged area according to the visual trait that has changed.
2403 */
2404 protected Rectangle[] getDamagedArea(ItemAppearence trait) {
2405
2406 if (trait == ItemAppearence.LinkChanged)
2407 return new Rectangle[] { getLinkDrawArea() }; // Invalidate area
2408 // where link is
2409 // drawn
2410
2411 return getDrawingArea();
2412
2413 }
2414
2415 public boolean hasVector() {
2416 return _overlay instanceof Vector;
2417 }
2418
2419 public boolean hasOverlay() {
2420 return _overlay != null;
2421 }
2422
2423 public Vector getVector() {
2424 if (_overlay instanceof Vector)
2425 return (Vector) _overlay;
2426 return null;
2427 }
2428
2429 public void setOverlay(Overlay overlay) {
2430 _overlay = overlay;
2431 }
2432
2433 public boolean dontSave() {
2434 // TODO Mike says: checkout if the ID check is still needed- When
2435 // will ID still
2436 // be -1 when saving a frame?
2437 // assert (i != null);
2438 return !isVisible() || getID() < 0 || offScreenTopOrLeft();
2439 }
2440
2441 public void setAnchorRight(Float anchor) {
2442 this._anchorRight = anchor;
2443 if (anchor != null)
2444 setX(FrameGraphics.getMaxFrameSize().width - anchor);
2445 }
2446
2447 public Float getAnchorRight() {
2448 return _anchorRight;
2449 }
2450
2451 public void setAnchorBottom(Float anchor) {
2452 this._anchorBottom = anchor;
2453 if (anchor != null)
2454 setY(FrameGraphics.getMaxFrameSize().height - anchor);
2455 }
2456
2457 public Float getAnchorBottom() {
2458 return _anchorBottom;
2459 }
2460
2461 public String getText() {
2462 return toString();
2463 }
2464
2465 public void setText(String text) {
2466 }
2467}
Note: See TracBrowser for help on using the repository browser.