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

Last change on this file since 821 was 821, checked in by jts21, 10 years ago

Add support for writetree to PDF2,
Links followed by writetree are clickable in the PDF (and have the Expeditee linkmark next to them).
To Fix:

  • The clickable bounding box of rotated images is just a non-rotated rectangle covering the entire rotated image (and also covering the whitespace or any other items around the corners of the image). This is due to anchors apparently not working when they're rotated (seriously, there's a way to rotate them, but once rotated they can't be clicked to follow their link)
  • Currently polygons don't get linked, because there doesn't seem to be a way to make a non-rectangular anchor, nor to actually put a polygon inside an anchor (I suppose it could be done by just putting an empty anchor of the correct dimensions over the top of the polygon)
File size: 79.4 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.IncorrectUseOfStatementException;
27import org.expeditee.actions.Javascript;
28import org.expeditee.actions.Misc;
29import org.expeditee.actions.Simple;
30import org.expeditee.gui.AttributeUtils;
31import org.expeditee.gui.AttributeValuePair;
32import org.expeditee.gui.DisplayIO;
33import org.expeditee.gui.Frame;
34import org.expeditee.gui.FrameGraphics;
35import org.expeditee.gui.FrameIO;
36import org.expeditee.gui.FrameKeyboardActions;
37import org.expeditee.gui.FrameMouseActions;
38import org.expeditee.gui.FrameUtils;
39import org.expeditee.gui.FreeItems;
40import org.expeditee.gui.MessageBay;
41import org.expeditee.gui.Overlay;
42import org.expeditee.gui.Vector;
43import org.expeditee.io.Conversion;
44import org.expeditee.settings.UserSettings;
45import org.expeditee.simple.Context;
46import org.expeditee.stats.AgentStats;
47import org.expeditee.stats.Formatter;
48import org.mozilla.javascript.Scriptable;
49import org.mozilla.javascript.ScriptableObject;
50
51/**
52 * Represents everything that can be drawn on the screen (text, lines, dots,
53 * images). Each specific type is a subclass of Item.
54 *
55 * @author jdm18
56 *
57 */
58public abstract class Item implements Comparable<Item>, Runnable {
59
60 public static final Float DEFAULT_THICKNESS = 2f;
61
62 public static final Float MINIMUM_THICKNESS = 0f;
63
64 public static final Float MINIMUM_PAINT_THICKNESS = 1f;
65
66 protected final int JOIN = BasicStroke.JOIN_ROUND;
67
68 protected final int CAP = BasicStroke.CAP_BUTT;
69
70 protected final Stroke DOT_STROKE = new BasicStroke(DEFAULT_THICKNESS,
71 CAP, JOIN, 4.0F);
72
73 protected final Stroke HIGHLIGHT_STROKE = new BasicStroke(
74 MINIMUM_THICKNESS, CAP, JOIN, 4.0F);
75
76 // contains all dots (including this one) that form an enclosure
77 // if this dot is part of an enclosing shape
78 private Collection<Item> _enclosure = null;
79
80 public static final int LEFT_MARGIN = 13;
81
82 // indicates which end the arrowhead should be drawn at
83 protected Polygon _poly = null;
84
85 protected boolean _connectedToAnnotation = false;
86
87 protected boolean _save = true;
88
89 private int _gradientAngle = 0;
90
91 public static final int NEAR_DISTANCE = 15;
92
93 /**
94 * The default Color to draw highlighting in
95 */
96 public static final int DEFAULT_HIGHLIGHT_THICKNESS = 2;
97
98 public static final Color DEFAULT_HIGHLIGHT = Color.RED;
99
100 public static final Color DEPRESSED_HIGHLIGHT = Color.GREEN;
101
102 public static final Color ALTERNATE_HIGHLIGHT = Color.BLUE;
103
104 public static final Color LINK_COLOR = Color.BLACK;
105
106 public static final Color ACTION_COLOR = Color.BLACK;
107
108 public static final Color LINK_ACTION_COLOR = Color.RED;
109
110 public static final Color DEFAULT_FOREGROUND = Color.BLACK;
111
112 public static final Color DEFAULT_BACKGROUND = Color.white;
113
114 public static final Color TRANSPARENT = new Color(0, 0, 0, 0);
115
116 /**
117 * The number of pixels highlighting should extend around Items.
118 */
119 public static final int XGRAVITY = 3;
120
121 public static final int MARGIN_RIGHT = 2;
122
123 public static final int MARGIN_LEFT = 15;
124
125 protected static final double DEFAULT_ARROWHEAD_RATIO = 0.3; // used to be 0.5
126
127 public static final double DEFAULT_ARROWHEAD_NIB_PERC = 0.75;
128
129 public static final Color GREEN = Color.GREEN.darker();
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 protected DotType _type = DotType.square;
145
146 protected boolean _filled = true;
147
148 private List<String> _tooltip = null;
149 private static Text _tooltipItem = null;
150 private static Item _tooltipOwner = null;
151
152 public enum AnchorEdgeType {
153 None, Left, Right, Top, Bottom
154 }
155
156 public static void DuplicateItem(Item source, Item dest) {
157 dest.setX(source.getX());
158 dest.setY(source.getY());
159
160 dest.setActions(source.getAction());
161 dest.setActionCursorEnter(source.getActionCursorEnter());
162 dest.setActionCursorLeave(source.getActionCursorLeave());
163 dest.setActionEnterFrame(source.getActionEnterFrame());
164 dest.setActionLeaveFrame(source.getActionLeaveFrame());
165 dest.setActionMark(source.getActionMark());
166
167 dest.setBackgroundColor(source.getBackgroundColor());
168 dest.setBottomShadowColor(source.getBottomShadowColor());
169 dest.setColor(source.getColor());
170 dest.setBorderColor(source.getBorderColor());
171
172 dest.setTooltips(source.getTooltip());
173 dest.setData(source.getData());
174 dest.setTag(source.getTag());
175 dest.setFillColor(source.getFillColor());
176 dest.setGradientColor(source.getGradientColor());
177 dest.setGradientAngle(source.getGradientAngle());
178 dest.setFillPattern(source.getFillPattern());
179
180 dest.setHighlight(source.getHighlight());
181 dest.setLink(source.getLink());
182 dest.setLinkFrameset(source.getLinkFrameset());
183 dest.setLinkMark(source.getLinkMark());
184 dest.setLinkTemplate(source.getLinkTemplate());
185
186 // dest.setMaxWidth(source.getMaxWidth());
187
188 dest.setOffset(source.getOffset());
189 // dest.setOwner(source.getOwner());
190 dest.setThickness(source.getThickness());
191 dest.setSize(source.getSize());
192 dest.setTopShadowColor(source.getTopShadowColor());
193 dest.setLinePattern(source.getLinePattern());
194
195 dest.setFloating(source.isFloating());
196 dest.setArrow(source.getArrowheadLength(), source.getArrowheadRatio(), source.getArrowheadNibPerc());
197
198 dest.setDotType(source.getDotType());
199 dest.setFilled(source.getFilled());
200 /*
201 * Calling the methods will move the item... This messes things up when
202 * the user uses backspace to delete a text line end
203 */
204 // dest._anchorLeft = source._anchorLeft;
205 // dest._anchorRight = source._anchorRight;
206 // dest._anchorTop = source._anchorTop;
207 // dest._anchorBottom = source._anchorBottom;
208 dest.setFormula(source.getFormula());
209 dest._overlay = source._overlay;
210 dest._mode = source._mode;// SelectedMode.None;
211 // dest._highlightColor = source._highlightColor;
212 // dest.setHighlight(source.getHighlight());
213
214 dest._visible = source._visible;
215
216 Frame parent = DisplayIO.getCurrentFrame();
217 if (parent == null)
218 parent = source.getParentOrCurrentFrame();
219 dest.setParent(parent);
220
221 /*
222 * TODO MIKE says maybe we could tighten up and only give items ID's if
223 * their current ID is negative?
224 */
225 if (parent != null) {
226 dest.setID(parent.getNextItemID());
227 }
228
229 if (parent != null && !UserSettings.UserName.equals(parent.getOwner())) {
230 dest.setOwner(UserSettings.UserName.get());
231 }
232 }
233
234 public void setGradientAngle(int gradientAngle) {
235 _gradientAngle = gradientAngle;
236
237 for (Line line : _lines) {
238 Item other = line.getOppositeEnd(this);
239 if (other.getGradientAngle() != gradientAngle)
240 other.setGradientAngle(gradientAngle);
241 }
242
243 invalidateCommonTrait(ItemAppearence.GradientColor);
244 invalidateFill();
245 }
246
247 public int getGradientAngle() {
248 return _gradientAngle;
249 }
250
251 public int getGravity() {
252 if (isVectorItem()) {
253 return 2;
254 }
255 return UserSettings.Gravity.get();
256 }
257
258 public static boolean showLineHighlight() {
259 return UserSettings.LineHighlight.get();
260 }
261
262 public enum HighlightMode {
263 None, Enclosed, Connected, Disconnect, Normal
264 }
265
266 public void setHighlightMode(HighlightMode mode) {
267 setHighlightMode(mode, DEFAULT_HIGHLIGHT);
268 }
269
270 protected Float _anchorLeft = null;
271 protected Float _anchorRight = null;
272
273 protected Float _anchorTop = null;
274 protected Float _anchorBottom = null;
275
276 protected HighlightMode _mode = HighlightMode.None;
277
278 private Point _offset = new Point(0, 0);
279
280 protected float _x;
281 protected float _y;
282
283 private int _id;
284
285 private Item _editTarget = this;
286
287 private String _creationDate = null;
288
289 private boolean _linkMark = true;
290
291 private boolean _actionMark = true;
292
293 private boolean _highlight = true;
294
295 // private int _maxWidth = -1;
296
297 private String _owner = null;
298
299 private String _link = null;
300
301 private boolean _linkHistory = true;
302
303 private StringBuffer _tag = new StringBuffer();
304
305 private List<String> _actionCursorEnter = null;
306
307 private List<String> _actionCursorLeave = null;
308
309 private List<String> _actionEnterFrame = null;
310
311 private List<String> _actionLeaveFrame = null;
312
313 private PermissionPair _permissionPair = null;
314
315 private UserAppliedPermission _overlayPermission = null;
316
317 public void setOverlayPermission(UserAppliedPermission overlayPermission) {
318 _overlayPermission = overlayPermission;
319 }
320
321 public void setPermission(PermissionPair permissionPair) {
322 _permissionPair = permissionPair;
323 }
324
325 public PermissionPair getPermission() {
326 return _permissionPair;
327 }
328
329 public UserAppliedPermission getUserAppliedPermission() {
330 String owner = _owner != null ? _owner : _parent != null ? _parent.getOwner() : null;
331 if(_permissionPair != null) return _permissionPair.getPermission(owner);
332 if(_overlayPermission != null) return _overlayPermission;
333 if(_parent != null) return _parent.getUserAppliedPermission();
334 return UserAppliedPermission.full;
335 }
336
337 public boolean hasPermission(UserAppliedPermission permission) {
338 return getUserAppliedPermission().ordinal() >= permission.ordinal();
339 }
340
341 // A fill color of null represents transparent
342 private Color _colorFill = null;
343
344 // A gradient color of null represents NO gradient
345 private Color _colorGradient = null;
346
347 // A fore color of null represents the default color
348 private Color _color = null;
349
350 protected Color _highlightColor = DEFAULT_HIGHLIGHT;
351
352 private Color _colorBackground = null;
353
354 private Color _colorBorder = null;
355
356 private Color _colorTopShadow = null;
357
358 private Color _colorBottomShadow = null;
359
360 // the link\action circle
361 private Polygon _circle = null;
362
363 // the invalid link cross
364 private Polygon _circleCross = null;
365
366 private Frame _parent = null;
367 private Frame _oldParent = null;
368
369 protected int _highlightThickness = 2;
370
371 protected int _vectorHighlightThickness = 1;
372
373 // arrowhead parameters
374 private float _arrowheadLength = 0;
375 private double _arrowheadRatio = DEFAULT_ARROWHEAD_RATIO;
376 private double _arrowheadNibPerc = DEFAULT_ARROWHEAD_NIB_PERC;
377
378 private Polygon _arrowhead = null;
379
380 // the list of lines that this point is part of.
381 private List<Line> _lines = new ArrayList<Line>();
382
383 private int[] _linePattern = null;
384
385 private boolean _floating = false;
386
387 // list of points constrained with this point
388 private List<Constraint> _constraints = new ArrayList<Constraint>();
389
390 private List<String> _actions = null;
391
392 private List<String> _data = null;
393
394 private String _formula = null;
395
396 private String _link_frameset = null;
397
398 private String _link_template = null;
399
400 private String _fillPattern = null;
401
402 private boolean _visible = true;
403
404 private float _thickness = -1.0F;
405
406 protected Item() {
407 _creationDate = Formatter.getLongDateTime();
408 }
409
410 /**
411 * Adds an action to this Item.
412 *
413 * @param action
414 * The action to add to this Item
415 */
416 public void addAction(String action) {
417 if (action == null || action.equals("")) {
418 return;
419 }
420
421 if (_actions == null) {
422 _actions = new LinkedList<String>();
423 }
424 _actions.add(action);
425 if (_actions.size() == 1) {
426 _poly = null;
427 invalidateCommonTrait(ItemAppearence.LinkChanged);
428 }
429 }
430
431 public void addAllConnected(Collection<Item> connected) {
432 if (!connected.contains(this))
433 connected.add(this);
434
435 for (Item item : getConnected()) {
436 if (!connected.contains(item))
437 item.addAllConnected(connected);
438 }
439 }
440
441 /**
442 * Adds the given Constraint to this Dot
443 *
444 * @param c
445 * The Constraint to set this Dot as a member of.
446 */
447 public void addConstraint(Constraint c) {
448 // do not add duplicate constraint
449 if (_constraints.contains(c))
450 return;
451
452 _constraints.add(c);
453 }
454
455 /**
456 * Adds a given line to the list of lines that this Point is an end for.
457 *
458 * @param line
459 * The Line that this Point is an end of.
460 */
461 public void addLine(Line line) {
462 if (_lines.contains(line)) {
463 return;
464 }
465
466 _lines.add(line);
467 }
468
469 /**
470 * Items are sorted by their Y coordinate on the screen.
471 *
472 * @param i
473 * The Item to compare this Item to
474 * @return a negative integer, zero, or a positive integer as this object is
475 * less than, equal to, or greater than the specified object.
476 */
477 public int compareTo(Item i) {
478 return getY() - i.getY();
479 }
480
481 /**
482 * Every Item has an area around it defined by a Shape (typically a
483 * rectangle), this method returns true if the given x,y pair lies within
484 * the area and false otherwise.
485 *
486 * @param x
487 * The x coordinate to check
488 * @param y
489 * The y coordinate to check
490 * @return True if the Shape around this Item contains the given x,y pair,
491 * false otherwise.
492 */
493 public boolean contains(int x, int y) {
494 return getPolygon().contains(x, y);
495 }
496
497 /**
498 * Returns a deep copy of this Item, note: it is up to the receiver to
499 * change the Item ID etc as necessary.
500 *
501 * @return A deep copy of this Item.
502 */
503 public abstract Item copy();
504
505 public void delete() {
506 _deleted = true;
507 }
508
509 @Override
510 public boolean equals(Object o) {
511 if (o == null)
512 return false;
513 if (getClass().equals(o.getClass())) {
514 Item i = (Item) o;
515 return i.getID() == getID()
516 && ((i.getParent() == _parent) || (i.getParent() != null && i
517 .getParent().equals(_parent)));
518 } else
519 return false;
520 }
521
522 /**
523 * Returns a list of any action code that is currently associated with this
524 * Item
525 *
526 * @return A List of action code associated with this Item, or null if none
527 * has been assigned.
528 */
529 public List<String> getAction() {
530 return _actions;
531 }
532
533 public List<String> getData() {
534 return _data;
535 }
536
537 public List<String> getActionCursorEnter() {
538 return _actionCursorEnter;
539 }
540
541 public List<String> getActionCursorLeave() {
542 return _actionCursorLeave;
543 }
544
545 public List<String> getActionEnterFrame() {
546 return _actionEnterFrame;
547 }
548
549 public List<String> getActionLeaveFrame() {
550 return _actionLeaveFrame;
551 };
552
553 public boolean getActionMark() {
554 return _actionMark;
555 }
556
557 /**
558 * Gets all the items connected to this item. Uses a recursive approach to
559 * search connected points.
560 *
561 * @return
562 */
563 public Collection<Item> getAllConnected() {
564 Collection<Item> list = new LinkedHashSet<Item>();
565 addAllConnected(list);
566 return list;
567 }
568
569 public Area getArea() {
570 return new Area(getPolygon());
571 }
572
573 public String getArrow() {
574 if (!hasVisibleArrow())
575 return null;
576
577 String ratio = "" + getArrowheadRatio();
578 if (ratio.length() - ratio.indexOf(".") > 2)
579 ratio = ratio.substring(0, ratio.indexOf(".") + 3);
580
581 return getArrowheadLength() + " " + ratio + " " + getArrowheadNibPerc();
582 }
583
584 public Polygon getArrowhead() {
585 return _arrowhead;
586 }
587
588 public float getArrowheadLength() {
589 return _arrowheadLength;
590 }
591
592 public double getArrowheadRatio() {
593 return _arrowheadRatio;
594 }
595
596 public double getArrowheadNibPerc() {
597 return _arrowheadNibPerc;
598 }
599
600 public Color getBackgroundColor() {
601 return _colorBackground;
602 }
603
604 public Color getBorderColor() {
605 return _colorBorder;
606 }
607
608 /**
609 * Returns the Color being used to shade the bottom half of this Item's
610 * border. This can be NULL if no Color is being used
611 *
612 * @return The Color displayed on the bottom\right half of this Item's
613 * border.
614 */
615 public Color getBottomShadowColor() {
616 return _colorBottomShadow;
617 }
618
619 /**
620 * Returns the height (in pixels) of this Item's surrounding area.
621 *
622 * @return The height (in pixels) of this Item's surrounding area as
623 * returned by getArea().
624 */
625 public int getBoundsHeight() {
626 return getPolygon().getBounds().height;
627 }
628
629 /**
630 * Returns the width (in pixels) of this Item's surrounding area.
631 *
632 * @return The width (in pixels) of this Item's surrounding area as returned
633 * by getArea().
634 */
635 public int getBoundsWidth() {
636 return getPolygon().getBounds().width;
637 }
638
639 // TODO draw the link with a circle rather than a polygon!!
640 public Polygon getLinkPoly() {
641 if (_circle == null) {
642 int points = 16;
643
644 double radians = 0.0;
645 int xPoints[] = new int[points];
646 int yPoints[] = new int[xPoints.length];
647
648 for (int i = 0; i < xPoints.length; i++) {
649 // circle looks best if these values are not related to gravity
650 xPoints[i] = (int) (3.5 * Math.cos(radians)) + 6;// (2 *
651 // GRAVITY);
652 yPoints[i] = (int) (3.5 * Math.sin(radians)) + 3;// GRAVITY;
653 radians += (2.0 * Math.PI) / xPoints.length;
654 }
655
656 _circle = new Polygon(xPoints, yPoints, xPoints.length);
657 }
658
659 return _circle;
660 }
661
662 protected Polygon getCircleCross() {
663
664 if (_circleCross == null) {
665 _circleCross = new Polygon();
666
667 Rectangle bounds = getLinkPoly().getBounds();
668 int x1 = (int) bounds.getMinX();
669 int x2 = (int) bounds.getMaxX();
670 int y1 = (int) bounds.getMinY();
671 int y2 = (int) bounds.getMaxY();
672 int midX = ((x2 - x1) / 2) + x1;
673 int midY = ((y2 - y1) / 2) + y1;
674
675 _circleCross.addPoint(x1, y1);
676 _circleCross.addPoint(x2, y2);
677 _circleCross.addPoint(midX, midY);
678 _circleCross.addPoint(x1, y2);
679 _circleCross.addPoint(x2, y1);
680 _circleCross.addPoint(midX, midY);
681 }
682
683 return _circleCross;
684 }
685
686 public Color getColor() {
687 return _color;
688 }
689
690 public Collection<Item> getConnected() {
691 List<Item> conn = new LinkedList<Item>();
692 conn.add(this);
693 conn.addAll(getEnclosures());
694 conn.addAll(getLines());
695 return conn;
696 }
697
698 public String getConstraintIDs() {
699 if (_constraints == null || _constraints.size() == 0)
700 return null;
701
702 String cons = "";
703
704 for (Constraint c : _constraints)
705 cons += c.getID() + " ";
706
707 return cons.trim();
708 }
709
710 /*
711 * public void setLinkValid(boolean val) { _isValidLink = val; }
712 */
713
714 /**
715 * Returns a List of any Constraints that this Dot is a memeber of.
716 *
717 * @return a List of Constraints that this Dot is a member of.
718 */
719 public List<Constraint> getConstraints() {
720 return _constraints;
721 }
722
723 public String getTag() {
724 if (_tag != null && _tag.length() > 0)
725 return _tag.toString();
726 return null;
727 }
728
729 public String getDateCreated() {
730 return _creationDate;
731 }
732
733 public Color getFillColor() {
734 return _colorFill;
735 }
736
737 public String getFillPattern() {
738 return _fillPattern;
739 }
740
741 public String getFirstAction() {
742 if (_actions == null || _actions.size() == 0)
743 return null;
744 return _actions.get(0);
745 }
746
747 public boolean getHighlight() {
748 return _highlight;
749 }
750
751 public Color getHighlightColor() {
752 if (_highlightColor.equals(getPaintColor()))
753 return getAlternateHighlightColor();
754 return getDefaultHighlightColor();
755 }
756
757 /**
758 * Returns the ID of this Item, which must be unique for the Frame.
759 *
760 * @return The ID of this Item.
761 */
762 public int getID() {
763 return _id;
764 }
765
766 /**
767 * Returns the list of IDs of the Lines that this Dot is an end of.
768 *
769 * @return The list of Line IDs that this point is part of.
770 */
771 public String getLineIDs() {
772 String lineID = null;
773
774 if (_lines.size() > 0) {
775 lineID = "" + _lines.get(0).getID();
776
777 for (int i = 1; i < _lines.size(); i++)
778 lineID += " " + _lines.get(i).getID();
779 }
780
781 return lineID;
782 }
783
784 public int[] getLinePattern() {
785 return _linePattern;
786 }
787
788 /**
789 * Returns a list of Lines where this Dot is an end.
790 *
791 * @return A list of the Lines that this Dot is an end for or null if no
792 * Lines have been added.
793 */
794 public List<Line> getLines() {
795 return _lines;
796 }
797
798 /**
799 * Returns the name of a Frame that this Item links to, or null if this Item
800 * has no link.
801 *
802 * @return The name of a Frame that this Item links to (if any) or null if
803 * this Item does not link to anything.
804 */
805 public String getLink() {
806 return _link;
807 }
808
809 public String getFormula() {
810 return _formula;
811 }
812
813 public boolean hasFormula() {
814 return _formula != null;
815 }
816
817 public boolean hasAttributeValuePair() {
818 return _attributeValuePair != null && _attributeValuePair.hasPair();
819 }
820
821 public void setFormula(String formula) {
822 _formula = formula;
823 }
824
825 public boolean calculate(String formula) {
826 setFormula(formula);
827 return true;
828 }
829
830 public String getLinkFrameset() {
831 return _link_frameset;
832 }
833
834 public boolean getLinkMark() {
835 return _linkMark;
836 }
837
838 public String getLinkTemplate() {
839 return _link_template;
840 }
841
842 // public int getMaxWidth() {
843 // return _maxWidth;
844 // }
845
846 public Point getOffset() {
847 return _offset;
848 }
849
850 public String getOwner() {
851 return _owner;
852 }
853
854 public Color getPaintBackgroundColor() {
855 Color colorBackground = getBackgroundColor();
856 if (colorBackground == null) {
857 if (getParent() != null && getParent().getBackgroundColor() != null)
858 return getParent().getBackgroundColor();
859
860 return DEFAULT_BACKGROUND;
861 }
862
863 return colorBackground;
864 }
865
866 /**
867 * Returns the foreground Color of this Item.
868 *
869 * @return The Color of this item (foreground)
870 */
871 public final Color getPaintColor() {
872 // If color is null then get the paint foregroundColor for the frame the
873 // item is on which is a color adjusted to suit the background
874 Color color = getColor();
875
876 if (color == null) {
877 if (getParent() != null)
878 return getParent().getPaintForegroundColor();
879
880 Frame current = DisplayIO.getCurrentFrame();
881 if (current == null) {
882 return DEFAULT_FOREGROUND;
883 }
884 return current.getPaintForegroundColor();
885 }
886
887 return color;
888 }
889
890 public final Color getPaintBorderColor() {
891 // If color is null then get the paint foregroundColor for the frame the
892 // item is on which is a color adjusted to suit the background
893 Color color = getBorderColor();
894
895 if (color == null) {
896 if (getParent() != null)
897 return getParent().getPaintForegroundColor();
898
899 Frame current = DisplayIO.getCurrentFrame();
900 if (current == null) {
901 return DEFAULT_FOREGROUND;
902 }
903 return current.getPaintForegroundColor();
904 }
905
906 return color;
907 }
908
909 protected Color getPaintHighlightColor() {
910 Color highlightColor = getDefaultHighlightColor();
911 if (hasVisibleBorder()) {
912 if (getPaintBorderColor().equals(highlightColor)) {
913 highlightColor = getDefaultHighlightColor();
914 }
915 } else if (getPaintBackgroundColor().equals(highlightColor)) {
916 highlightColor = getDefaultHighlightColor();
917 }
918 if (getParent() != null
919 && getParent().getPaintBackgroundColor().equals(highlightColor))
920 highlightColor = getParent().getPaintForegroundColor();
921
922 if (hasVisibleBorder()) {
923 if (highlightColor.equals(getBorderColor())
924 && getThickness() == getHighlightThickness()) {
925 highlightColor = new Color(highlightColor.getRed(),
926 highlightColor.getGreen(), highlightColor.getBlue(),
927 150);
928 }
929 }
930
931 return highlightColor;
932 }
933
934 static final int BRIGHTNESS = 185;
935
936 protected Color getDefaultHighlightColor() {
937 if (isVectorItem()
938 && !this.contains(FrameMouseActions.getX(), FrameMouseActions
939 .getY())) {
940 return new Color(255, BRIGHTNESS, BRIGHTNESS);
941 }
942 return _highlightColor;
943 }
944
945 protected Color getAlternateHighlightColor() {
946 if (isVectorItem()
947 && !this.contains(FrameMouseActions.getX(), FrameMouseActions
948 .getY())) {
949 return new Color(BRIGHTNESS, BRIGHTNESS, 255);
950 }
951 return ALTERNATE_HIGHLIGHT;
952 }
953
954 protected int getHighlightThickness() {
955 if (isVectorItem())
956 return _vectorHighlightThickness;
957 return _highlightThickness;
958 }
959
960 public final Frame getParent() {
961 return _parent;
962 }
963
964 public final Point getPosition() {
965 return new Point(getX(), getY());
966 }
967
968 /**
969 * Returns the size of this Item. For Text this is the Font size, for Lines
970 * and Dots this is the thickness.
971 *
972 * @return The size of this Item.
973 */
974 public float getSize() {
975 return -1.0F;
976 }
977
978 /**
979 * Returns the Color being used to shade the top half of this Item's border.
980 * This can be NULL if no Color is being used
981 *
982 * @return The Color displayed on the top\left half of this Item's border.
983 */
984 public Color getTopShadowColor() {
985 return _colorTopShadow;
986 }
987
988 public String getTypeAndID() {
989 return "T " + getID();
990 }
991
992 public Integer getWidthToSave() {
993 return getWidth();
994 }
995
996 public Integer getWidth() {
997 return null;
998 }
999
1000 public int getHeight() {
1001 return 0;
1002 }
1003
1004 /**
1005 * Returns the X coordinate of this Item on the screen
1006 *
1007 * @return The X coordinate of this Item on the screen
1008 */
1009 public int getX() {
1010 return Math.round(_x);
1011 }
1012
1013 /**
1014 * Returns the Y coordinate of this Item on the screen
1015 *
1016 * @return The Y coordinate of this Item on the screen
1017 */
1018 public int getY() {
1019 return Math.round(_y);
1020 }
1021
1022 public boolean hasVisibleArrow() {
1023 return isLineEnd() && getArrowheadRatio() != 0 && getArrowheadLength() != 0;
1024 }
1025
1026 /**
1027 * Checks if the given Shape intersects with the Shape around this Item.
1028 *
1029 * @param s
1030 * The Shape to check.
1031 * @return True if the two Shapes overlap, False otherwise.
1032 */
1033 public boolean intersects(Polygon p) {
1034 if (p == null)
1035 return false;
1036
1037 Area a = new Area(p);
1038 Area thisArea = this.getArea();
1039 // Need to do this check for circles
1040 if (a.equals(thisArea))
1041 return true;
1042
1043 a.intersect(thisArea);
1044
1045 // Need to check the second equality so that we dont pick up circles
1046 // inside other circles
1047 return !a.isEmpty() && !a.equals(new Area(p));
1048 }
1049
1050 /**
1051 * Note: Pictures always return False, as they should be drawn even when no
1052 * other annotation Items are.
1053 *
1054 * @return True if this Item is an annotation, False otherwise.
1055 */
1056 public boolean isAnnotation() {
1057 return false;
1058 }
1059
1060 public boolean isFloating() {
1061 return _floating;
1062 }
1063
1064 public boolean isFrameName() {
1065 if (this.getParent() == null || this.getParent().getNameItem() != this)
1066 return false;
1067 return true;
1068 }
1069
1070 public boolean isFrameTitle() {
1071 if (this.getParent() == null || this.getParent().getTitleItem() != this)
1072 return false;
1073 return true;
1074 }
1075
1076 /**
1077 * Returns True if this Item is currently highlighted.
1078 *
1079 * @return True if this Item is currently highlighted on the screen, False
1080 * otherwise.
1081 */
1082 public boolean isHighlighted() {
1083 if (isFloating())
1084 return false;
1085 return _mode != HighlightMode.None;
1086 }
1087
1088 /**
1089 * Tests if the item link is a valid framename, that is, the String must
1090 * begin with a character, end with a number with 0 or more letters and
1091 * numbers in between. If there is a dot in the framename all the chars
1092 * after it must be digits.
1093 *
1094 * @return True if the given framename is proper, false otherwise.
1095 */
1096 public boolean isLinkValid() {
1097 if (FrameIO.isPositiveInteger(getLink()))
1098 return true;
1099
1100 if (FrameIO.isValidFrameName(getLink()))
1101 return true;
1102 return false;
1103 }
1104
1105 public boolean isNear(int x, int y) {
1106
1107 int xLeft = getPolygon().getBounds().x;
1108 int yTop = getPolygon().getBounds().y;
1109
1110 return (x > xLeft - NEAR_DISTANCE && y > yTop - NEAR_DISTANCE
1111 && x < xLeft + getBoundsWidth() + NEAR_DISTANCE && y < yTop
1112 + getBoundsHeight() + NEAR_DISTANCE);
1113 }
1114
1115 public boolean isOldTag() {
1116 if (this instanceof Text)
1117 if (((Text) this).getTextList().get(0).toLowerCase().equals("@old"))
1118 return true;
1119 return false;
1120 }
1121
1122 /**
1123 * Merges this Item with the given Item. The merger Item should be left
1124 * unchanged after this method. The merger may or may not be the same class
1125 * as this Item, exact behaviour depends on the subclass, No-op is allowed.
1126 *
1127 * @param merger
1128 * The Item to merge with
1129 * @return any Item that should remain on the cursor
1130 */
1131 public abstract Item merge(Item merger, int mouseX, int mouseY);
1132
1133 /**
1134 * Displays this item directly on the screen. Note: All Items are
1135 * responsible for their own drawing, buffering, etc.
1136 *
1137 * @param g
1138 * The Graphics to draw this Item on.
1139 */
1140 public abstract void paint(Graphics2D g);
1141
1142 public void setTooltips(List<String> tooltips) {
1143 if (tooltips == null || tooltips.size() == 0) {
1144 _tooltip = null;
1145 } else {
1146 _tooltip = new LinkedList<String>(tooltips);
1147 }
1148 }
1149
1150 public void setTooltip(String tooltip) {
1151 if(_tooltip == null || _tooltip.size() == 0) {
1152 _tooltip = new LinkedList<String>();
1153 }
1154 if(tooltip != null && tooltip.trim().length() > 0) {
1155 _tooltip.add(tooltip);
1156 }
1157 }
1158
1159 public List<String> getTooltip() {
1160 return _tooltip;
1161 }
1162
1163 public static void clearTooltipOwner() {
1164 _tooltipOwner = null;
1165 }
1166
1167 public void paintTooltip(Graphics2D g) {
1168 if(_tooltipOwner != this) {
1169 _tooltipItem = null;
1170 _tooltipOwner = this;
1171 }
1172 // generate tooltip item
1173 if(_tooltipItem == null) {
1174 if(_tooltip != null && _tooltip.size() > 0) {
1175 StringBuffer tooltip = new StringBuffer();
1176 for(String t : _tooltip) {
1177 tooltip.append(t).append("\n");
1178 }
1179 if(tooltip.length() > 0) {
1180 tooltip.deleteCharAt(tooltip.length() - 1);
1181 }
1182 _tooltipItem = (Text) getParentOrCurrentFrame().getTooltipTextItem("");
1183 for(String s : _tooltip) {
1184 // set text
1185 if(s.trim().toLowerCase().startsWith("text") && s.contains(":")) {
1186 _tooltipItem.appendLine(s.substring(s.indexOf(':') + 1).trim());
1187 } else {
1188 AttributeUtils.setAttribute(_tooltipItem, new Text(s));
1189 }
1190 }
1191 } else {
1192 return;
1193 }
1194 }
1195 Rectangle bounds = getPolygon().getBounds();
1196 _tooltipItem.invalidateAll();
1197 int x = bounds.x + bounds.width;
1198 if(x + _tooltipItem.getPolygon().getBounds().width > FrameGraphics.getMaxFrameSize().width) {
1199 x -= x + _tooltipItem.getPolygon().getBounds().width - FrameGraphics.getMaxFrameSize().width;
1200 }
1201 int y = bounds.y + bounds.height + bounds.height / 2;
1202 if(y + _tooltipItem.getPolygon().getBounds().height > FrameGraphics.getMaxFrameSize().height) {
1203 y = bounds.y + bounds.height / 2 - _tooltipItem.getPolygon().getBounds().height;
1204 }
1205 _tooltipItem.setPosition(x, y);
1206 _tooltipItem.paint(g);
1207 }
1208
1209 public void paintFill(Graphics2D g) {
1210 Color fillColor = getFillColor();
1211 if (fillColor != null && getEnclosingDots() != null) {
1212 setFillPaint(g);
1213 g.fillPolygon(getEnclosedShape());
1214 }
1215 }
1216
1217 protected void setFillPaint(Graphics2D g) {
1218 Color fillColor = getFillColor();
1219 if (isFloating()) {
1220 // TODO experiment with adding alpha when picking up filled
1221 // items... Slows things down quite alot!!
1222 fillColor = new Color(fillColor.getRed(), fillColor.getGreen(),
1223 fillColor.getBlue(), fillColor.getAlpha());
1224 }
1225 g.setColor(fillColor);
1226 Color gradientColor = getGradientColor();
1227 if (gradientColor != null) {
1228 /*
1229 * It is slow when painting gradients... modify so this is only done
1230 * once unless it is resized...
1231 */
1232 Shape s = getEnclosedShape();
1233 if (s != null) {
1234 Rectangle b = s.getBounds();
1235 double rads = getGradientAngle() * Math.PI / 180;
1236 double cos = Math.cos(rads);
1237 double sin = Math.sin(rads);
1238
1239 GradientPaint gp = new GradientPaint((int) (b.x + b.width
1240 * (0.2 * cos + 0.5)), (int) (b.y + b.height
1241 * (0.2 * sin + 0.5)), fillColor, (int) (b.x + b.width
1242 * (-0.8 * cos + 0.5)), (int) (b.y + b.height
1243 * (-0.8 * sin + 0.5)), gradientColor);
1244 g.setPaint(gp);
1245 }
1246 }
1247 }
1248
1249 /**
1250 * This method performs all the actions in an items list. If it contains a
1251 * link as well the link is used as the source frame for all acitons.
1252 */
1253 public void performActions() {
1254 Frame sourceFrame = null;
1255 Item sourceItem = FreeItems.getItemAttachedToCursor();
1256
1257 if (sourceItem == null) {
1258 sourceItem = this;
1259 } else {
1260 for (Item i : sourceItem.getAllConnected()) {
1261 if (i instanceof Text) {
1262 sourceItem = i;
1263 break;
1264 }
1265 }
1266 }
1267
1268 // TODO decide whether to have items or
1269 // if a link exists make it the source frame for this action
1270 if (getLink() != null) {
1271 sourceFrame = FrameUtils.getFrame(getAbsoluteLink());
1272 }
1273 // if no link exists or the link is bad then use the
1274 // currently displayed frame as the source frame for the
1275 // action
1276 if (sourceFrame == null) {
1277 // For actions like format they rely on this being set to the
1278 // current frame incase the item being activated is on an overlay
1279 sourceFrame = DisplayIO.getCurrentFrame();
1280 }
1281
1282 for (String s : getAction()) {
1283 Object returnValue = Actions.PerformActionCatchErrors(sourceFrame,
1284 sourceItem, s);
1285 if (returnValue != null) {
1286 FreeItems.getInstance().clear();
1287 if (returnValue instanceof Item) {
1288 Misc.attachToCursor(((Item) returnValue).getAllConnected());
1289 } else if (returnValue instanceof Collection) {
1290 try {
1291 Misc.attachToCursor((Collection) returnValue);
1292 } catch (Exception e) {
1293 e.printStackTrace();
1294 }
1295 } else {
1296 Misc.attachStatsToCursor(returnValue.toString());
1297 }
1298 }
1299 }
1300 }
1301
1302 /**
1303 * Removes all constraints that this item has.
1304 *
1305 */
1306 public void removeAllConstraints() {
1307 while (_constraints.size() > 0) {
1308 Constraint c = _constraints.get(0);
1309 c.getEnd().removeConstraint(c);
1310 c.getStart().removeConstraint(c);
1311 }
1312 }
1313
1314 /**
1315 * Clears the list of Lines that this Dot is an end of. Note: This only
1316 * clears this Dot's list and does not have any affect on the Lines or other
1317 * Dots.
1318 */
1319 public void removeAllLines() {
1320 for (Line l : _lines) {
1321 l.invalidateAll();
1322 }
1323 _lines.clear();
1324 }
1325
1326 /**
1327 * Removes the given Constraint from the list of constraints that this Dot
1328 * is a part of.
1329 *
1330 * @param c
1331 * The Constraint that this Dot is no longer a part of.
1332 */
1333 public void removeConstraint(Constraint c) {
1334 _constraints.remove(c);
1335 }
1336
1337 /**
1338 * Removes the given Line from the list of lines that this Dot is an end
1339 * for.
1340 *
1341 * @param line
1342 * The Line that this Dot is no longer an end of.
1343 */
1344 public void removeLine(Line line) {
1345 if (_lines.remove(line))
1346 line.invalidateAll();
1347 }
1348
1349 public void run() {
1350 try {
1351
1352 List<String> action = this.getAction();
1353 if (action != null) {
1354 String action_name = action.get(0);
1355 if (action_name.equalsIgnoreCase("RunJavascriptFrame")){
1356 // Associate a new Context with this thread
1357 org.mozilla.javascript.Context javascript_context = org.mozilla.javascript.Context.enter();
1358 try {
1359 Scriptable javascript_scope = javascript_context.initStandardObjects();
1360 Context simple_context = new Context();
1361
1362
1363 //Object jsDisplayIO = org.mozilla.javascript.Context.javaToJS(org.expeditee.gui.DisplayIO, javascript_scope);
1364 //ScriptableObject.putProperty(javascript_scope, "displayIO", jsDisplayIO);
1365
1366
1367 Object jsSimpleContext = org.mozilla.javascript.Context.javaToJS(simple_context, javascript_scope);
1368 ScriptableObject.putProperty(javascript_scope, "simpleContext", jsSimpleContext);
1369
1370 Object jsErr = org.mozilla.javascript.Context.javaToJS(System.err, javascript_scope);
1371 ScriptableObject.putProperty(javascript_scope, "err", jsErr);
1372
1373 Object jsOut = org.mozilla.javascript.Context.javaToJS(System.out, javascript_scope);
1374 ScriptableObject.putProperty(javascript_scope, "out", jsOut);
1375
1376 Javascript.ProgramStarted();
1377 Javascript.RunFrameAndReportError(this, javascript_context,javascript_scope);
1378 MessageBay.displayMessage(AgentStats.getStats(), GREEN);
1379 }
1380 finally {
1381 org.mozilla.javascript.Context.exit();
1382 }
1383 }
1384 }
1385 else {
1386
1387 // assume it is a simple program that is to be run
1388 Simple.ProgramStarted();
1389 Context simple_context = new Context();
1390 Simple.RunFrameAndReportError(this, simple_context);
1391 MessageBay.displayMessage(AgentStats.getStats(), GREEN);
1392 }
1393 } catch (ConcurrentModificationException ce) {
1394 ce.printStackTrace();
1395 } catch (IncorrectUseOfStatementException ise) {
1396 MessageBay.linkedErrorMessage(ise.getMessage());
1397 MessageBay.displayMessage("See SIMPLE doc for ["
1398 + ise.getStatement() + "] statement", ise.getStatement()
1399 + "1", Color.CYAN.darker(), true, null);
1400 } catch (Exception e) {
1401 MessageBay.linkedErrorMessage(e.getMessage());
1402 }
1403 Simple.ProgramFinished();
1404 // Need to repaint any highlights etc
1405 FrameGraphics.requestRefresh(true);
1406 }
1407
1408 /**
1409 * Check if it has a relative link if so make it absolute.
1410 *
1411 */
1412 public void setAbsoluteLink() {
1413 String link = getLink();
1414 if (link == null)
1415 return;
1416 // Check if all the characters are digits and hence it is a relative
1417 // link
1418 if (!FrameIO.isPositiveInteger(link))
1419 return;
1420
1421 // Make it an absolute link
1422 String framesetName;
1423
1424 if (_parent == null)
1425 framesetName = DisplayIO.getCurrentFrame().getFramesetName();
1426 else
1427 framesetName = _parent.getFramesetName();
1428
1429 setLink(framesetName + link);
1430 }
1431
1432 /**
1433 * Sets any action code that should be associated with this Item Each entry
1434 * in the list is one line of code
1435 *
1436 * @param actions
1437 * The lines of code to associate with this Item
1438 */
1439 public void setActions(List<String> actions) {
1440 if (actions == null || actions.size() == 0) {
1441 invalidateCommonTrait(ItemAppearence.LinkChanged);
1442 _actions = null;
1443 } else
1444 _actions = new LinkedList<String>(actions);
1445
1446 // Want to resize the highlight box for text items if actions have been
1447 // added
1448 _poly = null;
1449 invalidateCommonTrait(ItemAppearence.LinkChanged);
1450 }
1451
1452 public void setData(List<String> data) {
1453 if (data == null || data.size() == 0)
1454 _data = null;
1455 else
1456 _data = new LinkedList<String>(data);
1457 }
1458
1459 public void setData(String data) {
1460 if (data == null || data.length() == 0)
1461 _data = null;
1462 else {
1463 _data = new LinkedList<String>();
1464 _data.add(data);
1465 }
1466 }
1467
1468 public void addToData(String dataItem) {
1469 if (dataItem != null) {
1470 if (_data == null)
1471 _data = new LinkedList<String>();
1472 _data.add(dataItem);
1473 }
1474 }
1475
1476 public void setActionCursorEnter(List<String> enter) {
1477 _actionCursorEnter = enter;
1478 }
1479
1480 public void setActionCursorLeave(List<String> leave) {
1481 _actionCursorLeave = leave;
1482 }
1483
1484 public void setActionEnterFrame(List<String> enter) {
1485 _actionEnterFrame = enter;
1486 }
1487
1488 public void setActionLeaveFrame(List<String> leave) {
1489 _actionLeaveFrame = leave;
1490 }
1491
1492 public void setActionMark(boolean val) {
1493 if (!val)
1494 invalidateCommonTrait(ItemAppearence.LinkChanged);
1495 _poly = null;
1496 _actionMark = val;
1497 if (val)
1498 invalidateCommonTrait(ItemAppearence.LinkChanged);
1499 }
1500
1501 /**
1502 * Sets whether this Item is an Annotation.
1503 *
1504 * @param val
1505 * True if this Item is an Annotation, False otherwise.
1506 */
1507 public abstract void setAnnotation(boolean val);
1508
1509 /**
1510 * Used to set this Line as an Arrow. If length and ratio are 0, no arrow is
1511 * shown.
1512 *
1513 * @param length
1514 * The how far down the shaft of the line the arrowhead should
1515 * come.
1516 * @param ratio
1517 * The ratio of the arrow's length to its width.
1518 */
1519 public void setArrow(float length, double ratio, double nib_perc) {
1520 _arrowheadLength = length;
1521 _arrowheadRatio = ratio;
1522 _arrowheadNibPerc = nib_perc;
1523 updateArrowPolygon();
1524 }
1525
1526 public void setArrow(float length, double ratio) {
1527 setArrow(length,ratio,DEFAULT_ARROWHEAD_NIB_PERC);
1528 }
1529
1530 public void setArrowhead(Polygon arrow) {
1531 _arrowhead = arrow;
1532 }
1533
1534 public void setArrowheadLength(float length) {
1535 _arrowheadLength = length;
1536 updateArrowPolygon();
1537 }
1538
1539 public void setArrowheadRatio(double ratio) {
1540 _arrowheadRatio = ratio;
1541 updateArrowPolygon();
1542 }
1543
1544 public void setArrowheadNibPerc(double perc) {
1545 _arrowheadNibPerc = perc;
1546 updateArrowPolygon();
1547 }
1548
1549 public void setBackgroundColor(Color c) {
1550 if (c != _colorBackground) {
1551 _colorBackground = c;
1552 invalidateCommonTrait(ItemAppearence.BackgroundColorChanged);
1553 }
1554 }
1555
1556 public void setBorderColor(Color c) {
1557 if (c != _colorBorder) {
1558 _colorBorder = c;
1559 invalidateCommonTrait(ItemAppearence.BorderColorChanged);
1560 }
1561 }
1562
1563 /**
1564 * Sets the Color to use on the bottom and right sections of this Item's
1565 * border. If top is NULL, then the Item's background Color will be used.
1566 *
1567 * @param top
1568 * The Color to display in the bottom and right sections of this
1569 * Item's border.
1570 */
1571 public void setBottomShadowColor(Color bottom) {
1572 _colorBottomShadow = bottom;
1573 }
1574
1575 /**
1576 * Sets the foreground Color of this Item to the given Color.
1577 *
1578 * @param c
1579 */
1580 public void setColor(Color c) {
1581 if (c != _color) {
1582 _color = c;
1583 invalidateCommonTrait(ItemAppearence.ForegroundColorChanged);
1584 if (hasVector()) {
1585 // TODO make this more efficient so it only repaints the items
1586 // for this vector
1587 FrameKeyboardActions.Refresh();
1588 }
1589 }
1590 }
1591
1592 public void setConstraintIDs(String IDs) {
1593 }
1594
1595 public void setConstraints(List<Constraint> constraints) {
1596 _constraints = constraints;
1597 }
1598
1599 public void setTag(String newData) {
1600 if (newData != null)
1601 _tag = new StringBuffer(newData);
1602 else
1603 _tag = null;
1604 }
1605
1606 /**
1607 * Sets the created date of this Frame to the given String.
1608 *
1609 * @param date
1610 * The date to use for this Frame.
1611 */
1612 public void setDateCreated(String date) {
1613 _creationDate = date;
1614 }
1615
1616 public void setFillColor(Color c) {
1617
1618 _colorFill = c;
1619
1620 for (Line line : _lines) {
1621 Item other = line.getOppositeEnd(this);
1622 if (other.getFillColor() != c) {
1623 other.setFillColor(c);
1624 }
1625 }
1626
1627 invalidateCommonTrait(ItemAppearence.FillColor);
1628 invalidateFill();
1629 }
1630
1631 public void setGradientColor(Color c) {
1632 _colorGradient = c;
1633
1634 for (Line line : _lines) {
1635 Item other = line.getOppositeEnd(this);
1636 if (other.getGradientColor() != c)
1637 other.setGradientColor(c);
1638 }
1639
1640 invalidateCommonTrait(ItemAppearence.GradientColor);
1641 invalidateFill();
1642 }
1643
1644 public Color getGradientColor() {
1645 return _colorGradient;
1646 }
1647
1648 public void setFillPattern(String patternLink) {
1649 _fillPattern = patternLink;
1650 invalidateCommonTrait(ItemAppearence.FillPattern);
1651 invalidateFill();
1652 }
1653
1654 public void setFloating(boolean val) {
1655 _floating = val;
1656 }
1657
1658 public void setHighlight(boolean val) {
1659 _highlight = val;
1660 }
1661
1662 /**
1663 * Sets the ID of this Item to the given Integer. Note: Items with ID's < 0
1664 * are not saved
1665 *
1666 * @param newID
1667 * The new ID to assign this Item.
1668 */
1669 public void setID(int newID) {
1670 _id = newID;
1671 }
1672
1673 /**
1674 * Sets the list of lines that this point is part of (may be set to null).
1675 *
1676 * @param lineID
1677 * A String of line ID numbers separated by spaces.
1678 */
1679 public void setLineIDs(String lineID) {
1680 }
1681
1682 public void setLinePattern(int[] pattern) {
1683 _linePattern = pattern;
1684
1685 for (Line line : getLines())
1686 line.setLinePattern(pattern);
1687 }
1688
1689 public void setLines(List<Line> lines) {
1690 _lines = lines;
1691
1692 for (Line line : lines)
1693 line.setLinePattern(getLinePattern());
1694
1695 }
1696
1697 /**
1698 * Links this item to the given Frame, this may be set to null to remove a
1699 * link.
1700 *
1701 * @param frameName
1702 * The name of the Frame to link this item to.
1703 */
1704 public void setLink(String frameName) {
1705 if (frameName == null) {
1706 invalidateCommonTrait(ItemAppearence.LinkChanged);
1707 }
1708
1709 // If a link is being removed or set then need to reset poly so the
1710 // highlighting is drawn with the correct width
1711 if (frameName == null || getLink() == null)
1712 _poly = null;
1713
1714 if (FrameIO.isValidLink(frameName))
1715 _link = frameName;
1716 else
1717 MessageBay.errorMessage("[" + frameName
1718 + "] is not a valid frame name");
1719 // TODO make this throw exceptions etc...
1720
1721 invalidateCommonTrait(ItemAppearence.LinkChanged);
1722 }
1723
1724 public void setLinkHistory(boolean value) {
1725 _linkHistory = value;
1726 }
1727
1728 public boolean getLinkHistory() {
1729 return _linkHistory;
1730 }
1731
1732 public void setLinkFrameset(String frameset) {
1733 if (frameset == null || FrameIO.isValidFramesetName(frameset))
1734 _link_frameset = frameset;
1735 else
1736 MessageBay.errorMessage("[" + frameset
1737 + "] is not a valid frameset name");
1738 // TODO make this throw exceptions etc...
1739 }
1740
1741 public void setLinkMark(boolean val) {
1742 if (!val)
1743 invalidateCommonTrait(ItemAppearence.LinkChanged);
1744 _poly = null;
1745 _linkMark = val;
1746 if (val)
1747 invalidateCommonTrait(ItemAppearence.LinkChanged);
1748 }
1749
1750 public void setLinkTemplate(String template) {
1751 if (FrameIO.isValidLink(template))
1752 _link_template = template;
1753 else
1754 MessageBay.errorMessage("[" + template
1755 + "] is not a valid frame name");
1756 // TODO make this throw exceptions etc...
1757 }
1758
1759 // /**
1760 // * Sets the maximum coordinates on the screen that this item may occupy.
1761 // * This is used by Text items to compute word-wrapping lengths.
1762 // *
1763 // * @param d
1764 // * The Maximum size of the Frame containing this Item.
1765 // */
1766 // public void setMaxWidth(int width) {
1767 // if (width > 0) {
1768 // _maxWidth = width;
1769 // updatePolygon();
1770 // }
1771 // }
1772
1773 public void setOffset(int x, int y) {
1774 _offset.setLocation(x, y);
1775 }
1776
1777 public void setOffset(Point p) {
1778 _offset.setLocation(p);
1779 }
1780
1781 public void setOwner(String own) {
1782 _owner = own;
1783 }
1784
1785 public void setParent(Frame frame) {
1786 _oldParent = _parent;
1787 _parent = frame;
1788
1789 if (_parent != null && UserSettings.UserName != null
1790 && !UserSettings.UserName.equals(_parent.getOwner())) {
1791 setOwner(UserSettings.UserName.get());
1792 }
1793 }
1794
1795 /**
1796 * Invalidates this, connected lines and fill
1797 *
1798 * @param trait
1799 */
1800 private void invalidateCommonTraitForAll(ItemAppearence trait) {
1801 invalidateCommonTrait(trait);
1802 if (isLineEnd()) {
1803 boolean hasLinePattern = getLines().get(0).getLinePattern() != null;
1804 if (hasLinePattern) {
1805 for (Item i : getAllConnected()) {
1806 if (i instanceof Line) {
1807 ((Line) i).invalidateCommonTrait(trait);
1808 }
1809 }
1810 } else {
1811 for (Line line : getLines()) {
1812 line.invalidateCommonTrait(trait);
1813 }
1814 }
1815 }
1816 if (_colorFill != null) {
1817 invalidateFill(); // only invalidates if has fill
1818 }
1819 for (XRayable x : getEnclosures()) {
1820 x.invalidateCommonTrait(trait);
1821 }
1822
1823 }
1824
1825
1826
1827
1828
1829 protected void anchorConstraints()
1830 {
1831 // update the position of any dots that are constrained by this one
1832 for (Constraint c : _constraints) {
1833 Item other = c.getOppositeEnd(this);
1834
1835 // only set position if the other dot is still fixed to the
1836 // frame
1837 if (/* this.isFloating() && */!other.isFloating()) {
1838 if (c.getType() == Constraint.HORIZONTAL) {
1839 if (isAnchoredY()) {
1840 // Make the 'other' item have the same anchor top/bottom values as this
1841 other._anchorTop = _anchorTop;
1842 other._anchorBottom = _anchorBottom;
1843 }
1844 } else if (c.getType() == Constraint.VERTICAL) {
1845 if (isAnchoredX()) {
1846 // Make the 'other' item have the same anchor left/right values as this
1847 other._anchorLeft = _anchorLeft;
1848 other._anchorRight = _anchorRight;
1849 }
1850 } else if (c.isDiagonal()) {
1851
1852 System.err.println("Warning: anchorConstraints() not implement for Diagonal setting");
1853 }
1854 }
1855 }
1856 }
1857
1858 /**
1859 * Sets the position of this item on the screen
1860 *
1861 * @param x
1862 * The new X coordinate
1863 * @param y
1864 * The new Y coordinate
1865 */
1866 public void setPosition(float x, float y) {
1867 float deltaX = x - _x;
1868 float deltaY = y - _y;
1869
1870 if (deltaX == 0 && deltaY == 0)
1871 return;
1872
1873 invalidateCommonTraitForAll(ItemAppearence.PreMoved);
1874
1875 _x = x;
1876 _y = y;
1877
1878 for (Item i : getEnclosures()) {
1879 i.updatePolygon();
1880 }
1881 updatePolygon();
1882
1883 // update the position of any dots that are constrained by this one
1884 for (Constraint c : _constraints) {
1885 Item other = c.getOppositeEnd(this);
1886
1887 // only set position if the other dot is still fixed to the
1888 // frame
1889 if (/* this.isFloating() && */!other.isFloating()) {
1890 if (c.getType() == Constraint.HORIZONTAL) {
1891 if (other._y != y) {
1892 other.setY(y);
1893 }
1894 } else if (c.getType() == Constraint.VERTICAL) {
1895 if (other._x != x) {
1896 other.setX(x);
1897 }
1898 } else if (c.isDiagonal()) {
1899 if (Math.abs(other._x - x) != Math.abs(other._y - y)) {
1900
1901 float m1 = c.getGradient();
1902 float c1 = y - m1 * x;
1903 // Now work out the equation for the second line
1904 // Get the first line the other end is attached to that
1905 // is not the diagonal line
1906 List<Line> lines = other.getLines();
1907 // If there is only one line...
1908 if (lines.size() == 1) {
1909 if (m1 != 0) {
1910 if (Math.abs(deltaX) > Math.abs(deltaY)) {
1911 other.setX((other._y - c1) / m1);
1912 } else {
1913 other.setY(m1 * other._x + c1);
1914 }
1915 }
1916 } else if (lines.size() > 1) {
1917 Line otherLine = lines.get(0);
1918 Item end = otherLine.getOppositeEnd(other);
1919 if (end.equals(this)) {
1920 otherLine = lines.get(1);
1921 end = otherLine.getOppositeEnd(other);
1922 assert (!end.equals(this));
1923 }
1924
1925 float xDiff = end._x - other._x;
1926 float yDiff = end._y - other._y;
1927 if (xDiff == 0) {
1928 other.setY(m1 * other._x + c1);
1929 } else if (Math.abs(xDiff) == Math.abs(yDiff)
1930 && !this.isFloating() && deltaX == 0
1931 && deltaY == 0) {
1932 if (deltaX == 0) {
1933 _x = (_y - other._y) * m1 + other._x;
1934 } else {
1935 _y = (_x - other._x) * m1 + other._y;
1936 }
1937 } else {
1938 float m2 = yDiff / xDiff;
1939 float c2 = end._y - m2 * end._x;
1940 float mDiff = m1 - m2;
1941 if (Math.abs(mDiff) < 0.000001) {
1942 assert (false);
1943 // TODO how do I handle this case!!
1944 } else {
1945 float newX = (c2 - c1) / mDiff;
1946 float newY = m1 * newX + c1;
1947 if (other._x != newX
1948 /* && other._y != newY */) {
1949 other.setPosition(newX, newY);
1950 }
1951 }
1952 }
1953 }
1954 // Do simultaneous equations to get the new postion for
1955 // the other end of the diagonal line
1956 }
1957 }
1958 }
1959 }
1960
1961 for (Line line : getLines()) {
1962 line.updatePolygon();
1963 }
1964
1965 // for (Item item : getAllConnected()) {
1966 // item.updatePolygon();
1967 // }
1968
1969 invalidateCommonTraitForAll(ItemAppearence.PostMoved);
1970
1971 }
1972
1973 public void setPosition(Point position) {
1974 setPosition(position.x, position.y);
1975 }
1976
1977 public void setRelativeLink() {
1978 String link = getLink();
1979 if (link == null)
1980 return;
1981 assert (_parent != null);
1982
1983 if (FrameIO.isPositiveInteger(link))
1984 return;
1985
1986 // Check if the link is for the current frameset
1987 if (_parent.getFramesetName().equalsIgnoreCase(
1988 Conversion.getFramesetName(link))) {
1989 setLink("" + Conversion.getFrameNumber(link));
1990 }
1991 }
1992
1993 /**
1994 * Sets the size of this Item. For Text this is the Font size. For Lines and
1995 * Dots this is the thickness.
1996 */
1997 public void setSize(float size) {
1998 }
1999
2000 /**
2001 * Sets the thickness of the item.
2002 *
2003 * @param thick
2004 */
2005 public final void setThickness(float thick) {
2006 setThickness(thick, true);
2007 }
2008
2009 /**
2010 * Sets the thickness of this item.
2011 *
2012 * @param thick
2013 * the new thickness for the item
2014 * @param setConnectedThickness
2015 * true if all items connected to this item should also have
2016 * their thickness set
2017 */
2018 public void setThickness(float thick, boolean setConnectedThickness) {
2019 if (thick == _thickness)
2020 return;
2021 boolean bigger = thick > _thickness;
2022
2023 if (!bigger) {
2024 if (setConnectedThickness) {
2025 // TODO is there a more efficient way of doing this?
2026 for (Item i : getConnected())
2027 i.invalidateCommonTrait(ItemAppearence.Thickness);
2028 } else {
2029 invalidateCommonTrait(ItemAppearence.Thickness);
2030 }
2031 }
2032
2033 _thickness = thick;
2034 // update the size of any lines
2035 /*
2036 * TODO: Revise the way line thickness is set to make it more efficient
2037 * etc...
2038 */
2039 for (Line line : getLines())
2040 line.setThickness(thick, setConnectedThickness);
2041
2042 if (setConnectedThickness)
2043 updatePolygon();
2044
2045 if (bigger) {
2046 if (setConnectedThickness) {
2047 for (Item i : getConnected())
2048 i.invalidateCommonTrait(ItemAppearence.Thickness);
2049 } else {
2050 invalidateCommonTrait(ItemAppearence.Thickness);
2051 }
2052 }
2053 }
2054
2055 /**
2056 * Returns the thickness (in pixels) of this Dot.
2057 *
2058 * @return The 'thickness' of this Dot. (returns -1 if the thickness is not
2059 * set).
2060 */
2061 public float getThickness() {
2062 return _thickness;
2063 }
2064
2065 /**
2066 * Sets the Color to use on the top and left sections of this Item's border.
2067 * If top is NULL, then the Item's background Color will be used.
2068 *
2069 * @param top
2070 * The Color to display in the top and left sections of this
2071 * Item's border.
2072 */
2073 public void setTopShadowColor(Color top) {
2074 _colorTopShadow = top;
2075 }
2076
2077 public void setWidth(Integer width) throws UnsupportedOperationException {
2078 throw new UnsupportedOperationException(
2079 "Item type does not support width attribute!");
2080 }
2081
2082 public void setRightMargin(int i, boolean fixWidth) {
2083 int newWidth = i - getX() - Item.MARGIN_LEFT;
2084 if (!fixWidth) {
2085 newWidth *= -1;
2086 }
2087
2088 setWidth(newWidth);
2089 }
2090
2091 /**
2092 * Sets the position of this Item on the X axis
2093 *
2094 * @param newX
2095 * The position on the X axis to assign to this Item
2096 */
2097 public void setX(float newX) {
2098 setPosition(newX, getY());
2099 }
2100
2101 /**
2102 * Sets the position of this Item on the Y axis
2103 *
2104 * @param newY
2105 * The position on the Y axis to assign to this Item
2106 */
2107 public void setY(float newY) {
2108 setPosition(getX(), newY);
2109 }
2110
2111 /**
2112 * Paints any highlighting of this Item. This may include changing the
2113 * thickness (lines) or painting a box around the item (Text, Images). If
2114 * val is True then the Graphics Color is changed to the highlight Color, if
2115 * False then the Graphics Color is left unchanged (for clearing of
2116 * highlighting).
2117 *
2118 * @param val
2119 * True if this Item should be highlighted, false if the
2120 * highlighting is being cleared.
2121 * @return The desired mouse cursor when this Item is highlighted (negative
2122 * means no change)
2123 */
2124 public int setHighlightColor() {
2125 return setHighlightColor(DEFAULT_HIGHLIGHT);
2126 }
2127
2128 public int setHighlightColor(Color c) {
2129 if (!this.isVisible() && this.hasVector()) {
2130 for (Item i : this.getParentOrCurrentFrame().getVectorItems()) {
2131 if (i.getEditTarget() == this) {
2132 i.setHighlightColor(c);
2133 }
2134 }
2135 }
2136
2137 _highlightThickness = DEFAULT_HIGHLIGHT_THICKNESS;
2138
2139 Color selColor = (c != null) ? c : DEFAULT_HIGHLIGHT;
2140 if (_highlightColor != c) {
2141 _highlightColor = selColor;
2142 this.invalidateCommonTrait(ItemAppearence.HighlightColorChanged);
2143 }
2144
2145 return Item.UNCHANGED_CURSOR;
2146
2147 }
2148
2149 private void updateArrowPolygon() {
2150 if (getArrowheadLength() < 0 || getArrowheadRatio() < 0 || getArrowheadNibPerc() < 0)
2151 _arrowhead = null;
2152 else {
2153 _arrowhead = Line.createArrowheadPolygon(getX(),getY(),getArrowheadLength(),getArrowheadRatio(),getArrowheadNibPerc());
2154 }
2155 }
2156
2157 public abstract void updatePolygon();
2158
2159 public void setHidden(boolean state) {
2160 this._visible = !state;
2161 }
2162
2163 public void setVisible(boolean state) {
2164 this._visible = state;
2165 }
2166
2167 public boolean isVisible() {
2168 return _visible && !_deleted;
2169 }
2170
2171 /**
2172 * Raised whenever the item is removed, added, no longer in view (That is,
2173 * when it is not on any of the current frames, of overlays of the current
2174 * frames) or has become visible. That is, when it is either on a current
2175 * frame, or an overlay of a current frame.
2176 *
2177 * @param e
2178 * The event
2179 */
2180 public void onParentStateChanged(ItemParentStateChangedEvent e) {
2181 }
2182
2183 public void setHighlightMode(HighlightMode mode, Color color) {
2184 setHighlightColor(color);
2185 if (hasPermission(UserAppliedPermission.followLinks)
2186 || getEditTarget().hasPermission(UserAppliedPermission.followLinks)) {
2187 if (_mode != mode) {
2188 _mode = mode;
2189 this.invalidateCommonTrait(ItemAppearence.HighlightModeChanged);
2190 }
2191 }
2192 }
2193
2194 public HighlightMode getHighlightMode() {
2195 return _mode;
2196 }
2197
2198 public void anchor() {
2199 Frame current = getParentOrCurrentFrame();
2200 // only set the id if we've moved to a different frame, or if the frame already has an item with that id
2201 if(!current.equals(_oldParent) || current.getItemWithID(getID()) != null) {
2202 int id = _id;
2203 setID(current.getNextItemID());
2204 // System.out.println(this + " - Set ID to " + _id + " (was " + id + ")");
2205 } else {
2206 // System.out.println(this + " - Kept old ID of " + _id);
2207 }
2208 setOffset(0, 0);
2209 setParent(current);
2210
2211 current.addItem(this, false);
2212 current.setResort(true);
2213 setRelativeLink();
2214 setFloating(false);
2215
2216 // // If its an unconstrained line end check if we should add a
2217 // constraint
2218 // if (isLineEnd() && getLines().size() <= 2
2219 // && getConstraints().size() <= 1) {
2220 // Constraint existingConstraint = null;
2221 // List<Constraint> constraints = getConstraints();
2222 // // Get the existing constraint
2223 // if (constraints.size() > 0) {
2224 // existingConstraint = constraints.get(0);
2225 // }
2226 // for (Line line : getLines()) {
2227 // Integer constraintType = line.getPossibleConstraint();
2228 // if (constraintType != null) {
2229 // Item oppositeEnd = line.getOppositeEnd(this);
2230 // if (existingConstraint == null
2231 // || !existingConstraint.contains(oppositeEnd)) {
2232 // new Constraint(this, oppositeEnd,
2233 // getParentOrCurrentFrame().getNextItemID(),
2234 // constraintType);
2235 // }
2236 // }
2237 // }
2238 // }
2239 }
2240
2241 /**
2242 * Gets the parent frame if it is set or the current frame if this item does
2243 * not have a parent set.
2244 *
2245 * @return
2246 */
2247 public Frame getParentOrCurrentFrame() {
2248 // if the item is from an overlay the parent will NOT be null
2249 if (getParent() == null) {
2250 return DisplayIO.getCurrentFrame();
2251 }
2252 return getParent();
2253 }
2254
2255 /**
2256 * Sets the list of Dots (including this one) that form a closed shape.
2257 * Passing null sets this dot back to its normal (non-enclosed) state.
2258 *
2259 * @param enclosed
2260 * The List of Dots including this one that form a closed shape,
2261 * or null.
2262 */
2263 public void setEnclosedList(Collection<Item> enclosed) {
2264
2265 boolean changed = (_enclosure == null && enclosed != null);
2266
2267 if (_enclosure != null && enclosed == null) {
2268 invalidateFill();
2269 }
2270
2271 _enclosure = enclosed;
2272
2273 if (changed) {
2274 invalidateFill();
2275 ;
2276 }
2277 }
2278
2279 /**
2280 * Returns the polygon that represents the shape created by all the Dots in
2281 * this Dot's enclosed list. If the list is null, then null is returned.
2282 *
2283 * @return A Polygon the same shape and position as created by the Dots in
2284 * the enclosed list.
2285 */
2286 public Polygon getEnclosedShape() {
2287 if (_enclosure == null)
2288 return null;
2289
2290 Polygon poly = new Polygon();
2291 for (Item d : _enclosure) {
2292 poly.addPoint(d.getX(), d.getY());
2293 }
2294
2295 return poly;
2296 }
2297
2298 /**
2299 * Returns the list of Dots that, along with this Dot, form an enclosed
2300 * polygon. If this Dot is not part of an enclosure null may be returned.
2301 *
2302 * @return The List of Dots that form an enclosed shape with this Dot, or
2303 * null if this Dot is not part of an enclosure.
2304 */
2305 public Collection<Item> getEnclosingDots() {
2306 return _enclosure;
2307 }
2308
2309 /**
2310 * Returns whether this Dot has an assigned enclosure list of other Dots.
2311 * The result is the same as getEnclosedShape() != null.
2312 *
2313 * @return True if this Dot has an enclosure list of other Dots, false
2314 * otherwise.
2315 */
2316 public boolean isEnclosed() {
2317 return _enclosure != null;
2318 }
2319
2320 /**
2321 * True if this item is the end of a line.
2322 *
2323 * @return
2324 */
2325 public boolean isLineEnd() {
2326 // TODO this will need to be redone when enclosure class is added...
2327 // At the moment enclosures are only circles...we don't want circle
2328 // centres to be lineEnds
2329 return _lines.size() > 0;
2330 }
2331
2332 public boolean hasEnclosures() {
2333 return _enclosures.size() > 0;
2334 }
2335
2336 /**
2337 * Method that is called to notify an item that is on the end of a line that
2338 * its line has changed color.
2339 *
2340 * @param c
2341 * the new color for the line
2342 */
2343 protected void lineColorChanged(Color c) {
2344 for (Line l : getLines()) {
2345 if (l.getColor() != c)
2346 l.setColor(c);
2347 }
2348 }
2349
2350 /**
2351 * Checks if this item is off the left or top of the screen
2352 *
2353 * @return
2354 */
2355 public boolean offScreenTopOrLeft() {
2356 Rectangle itemRect = getArea().getBounds();
2357 // Check that the bottom right corner of this item is on the screen
2358 if (itemRect.x + itemRect.width >= 0
2359 && itemRect.y + itemRect.height >= 0)
2360 return false;
2361 // Check if all the items it is connected to are offscreen
2362 for (Item i : getAllConnected()) {
2363 Rectangle iRect = i.getArea().getBounds();
2364 // Check that the bottom right corner of this item is on the screen
2365 if (iRect.x + iRect.width >= 0 && iRect.y + iRect.height >= 0) {
2366 return false;
2367 }
2368 }
2369 return true;
2370 }
2371
2372 public void setConnectedToAnnotation(boolean val) {
2373 _connectedToAnnotation = val;
2374 }
2375
2376 public boolean isConnectedToAnnotation() {
2377 return _connectedToAnnotation;
2378 }
2379
2380 public boolean hasAction() {
2381 List<String> actions = getAction();
2382 return actions != null && actions.size() > 0;
2383 }
2384
2385 public void setAction(String action) {
2386 // Want to resize the highlight box for text items if actions are been
2387 // added
2388 if (action == null || action.length() == 0) {
2389 invalidateCommonTrait(ItemAppearence.LinkChanged);
2390 }
2391 if (_actions == null || _actions.size() == 0) {
2392 _poly = null;
2393 _actions = new LinkedList<String>();
2394 } else {
2395 _actions.clear();
2396 }
2397 if (action != null && action.length() > 0)
2398 _actions.add(action);
2399 invalidateCommonTrait(ItemAppearence.LinkChanged);
2400 }
2401
2402 protected int getLinkYOffset() {
2403 return 0;
2404 }
2405
2406 protected Rectangle getLinkDrawArea() {
2407 return getLinkDrawArea(getX() - LEFT_MARGIN, getY() + getLinkYOffset());
2408 }
2409
2410 /**
2411 * TODO: Revise - it would be good to have a member that defines the link
2412 * dimensions.
2413 *
2414 * @param x
2415 * Left of graphic (i.e not centered)
2416 * @param y
2417 * Above graphic (i.e not centered)
2418 *
2419 * @return The drawing area of the link at the given coordinates.
2420 */
2421 public Rectangle getLinkDrawArea(int x, int y) {
2422 return new Rectangle(x + 2, y - 1, 8, 8);
2423 }
2424
2425 /**
2426 * Paint the link symbol for the item if it is a
2427 *
2428 * @param g
2429 */
2430 protected void paintLink(Graphics2D g) {
2431 paintLinkGraphic(g, getX() - LEFT_MARGIN, getY() + getLinkYOffset());
2432 }
2433
2434 /**
2435 * Paint the link symbol for the item at a given position.
2436 *
2437 * @see #paintLink
2438 *
2439 * @param g
2440 * The graphics to paint with
2441 *
2442 * @param x
2443 * The x position of the link. Left of graphic (i.e not centered)
2444 *
2445 * @param y
2446 * The y position of the link. Above of graphic (i.e not
2447 * centered)
2448 */
2449 public void paintLinkGraphic(Graphics2D g, int x, int y) {
2450
2451 boolean hasLink = getLink() != null;
2452 boolean hasAction = hasAction();
2453
2454 if (hasLink || hasAction) {
2455 g.setStroke(HIGHLIGHT_STROKE);
2456 if (hasLink && hasAction) {
2457 g.setColor(LINK_ACTION_COLOR);
2458 } else if (hasLink) {
2459 g.setColor(LINK_COLOR);
2460 } else if (hasAction) {
2461 g.setColor(ACTION_COLOR);
2462 }
2463
2464 AffineTransform at = new AffineTransform();
2465 AffineTransform orig = g.getTransform();
2466 at.translate(x, y);
2467 g.setTransform(at);
2468
2469 if (getLinkMark() && getLink() != null) {
2470 g.drawPolygon(getLinkPoly());
2471
2472 // if the link is not valid, cross out the circle
2473 if (!isLinkValid())
2474 g.drawPolygon(getCircleCross());
2475 }
2476
2477 if (getActionMark() && hasAction()) {
2478 g.drawPolygon(getLinkPoly());
2479 g.fillPolygon(getLinkPoly());
2480
2481 // if the link is not valid, cross out the circle
2482 if (!isLinkValid() && getLink() != null) {
2483 g.setColor(getParent().getPaintBackgroundColor());
2484 g.drawPolygon(getCircleCross());
2485 }
2486 }
2487
2488 // reset the graphics tranformation
2489 g.setTransform(orig);
2490 }
2491 }
2492
2493 /**
2494 * Gets the distance between the start of the text and the left border of
2495 * the item. This distance changes depending on whether or not the item is
2496 * linked or has an associated action.
2497 *
2498 * @return the gap size in pixels
2499 */
2500 protected int getLeftMargin() {
2501 return ((getLinkMark() && getLink() != null)
2502 || (getActionMark() && getAction() != null) ? MARGIN_LEFT
2503 - MARGIN_RIGHT : MARGIN_RIGHT);
2504 }
2505
2506 public String getName() {
2507 return getText();
2508 }
2509
2510 final public String getAbsoluteLinkTemplate() {
2511 return getAbsoluteLink(getLinkTemplate());
2512 }
2513
2514 final public String getAbsoluteLinkFrameset() {
2515 return getAbsoluteLink(getLinkFrameset());
2516 }
2517
2518 final public String getAbsoluteLink() {
2519 return getAbsoluteLink(getLink());
2520 }
2521
2522 /**
2523 * @param link
2524 * @return
2525 */
2526 private String getAbsoluteLink(String link) {
2527 if (link == null)
2528 return null;
2529 // assert (_parent!= null);
2530 Frame parent = getParentOrCurrentFrame();
2531 if (_parent == null) {
2532 // if parent is null it is an item on the message box
2533 // so it must already be absolute
2534 // assert (!FrameIO.isPositiveInteger(link));
2535 // return link;
2536
2537 }
2538
2539 // if its a relative link then return absolute
2540 if (FrameIO.isPositiveInteger(link)) {
2541 return parent.getFramesetName() + link;
2542 }
2543 return link;
2544 }
2545
2546 public static String convertToAbsoluteLink(String link) {
2547 if (link == null)
2548 return null;
2549 // assert (_parent!= null);
2550 Frame parent = DisplayIO.getCurrentFrame();
2551 assert (parent != null);
2552
2553 // if its a relative link then return absolute
2554 if (FrameIO.isPositiveInteger(link)) {
2555 return parent.getFramesetName() + link;
2556 }
2557 return link;
2558 }
2559
2560 /**
2561 * Sets the x and y values of this item ignoring constraints.
2562 *
2563 * @param x
2564 * new x position
2565 * @param y
2566 * new y position
2567 */
2568 public void setXY(float x, float y) {
2569 _x = x;
2570 _y = y;
2571 }
2572
2573 /**
2574 * Recursive function for getting the path around a shape. This is used to
2575 * get the path that is painted on the screen.
2576 *
2577 * @param visited
2578 * @param points
2579 * @param addToEnd
2580 * @param toExplore
2581 */
2582 public void appendPath(Collection<Line> visited, LinkedList<Point> points,
2583 boolean addToEnd, Collection<Line> toExplore) {
2584
2585 if (addToEnd) {
2586 // put the start item points into our list
2587 points.addLast(getPosition());
2588 } else {
2589 points.addFirst(getPosition());
2590 }
2591
2592 // Find the line that has not been added yet
2593 LinkedList<Line> lines = new LinkedList<Line>();
2594 lines.addAll(getLines());
2595
2596 while (!lines.isEmpty()) {
2597 Line l = lines.remove();
2598 // if we havnt visited the line yet visit it
2599 if (!visited.contains(l)) {
2600 visited.add(l);
2601 Item otherEnd = l.getOppositeEnd(this);
2602 // Add all the enexplored lines to our list
2603 while (!lines.isEmpty()) {
2604 l = lines.remove();
2605 // Get the paths for the rest of the lines to be explored
2606 // later
2607 if (!toExplore.contains(l) && !visited.contains(l)) {
2608 toExplore.add(l);
2609 }
2610 }
2611 otherEnd.appendPath(visited, points, addToEnd, toExplore);
2612 }
2613 }
2614 }
2615
2616 /**
2617 * Gets the size of the enclosure that this item is part of. Used to
2618 * determine the paint order of items, with smaller items being painted
2619 * first.
2620 *
2621 * @return the area of the box surrounding the enclosed shape that this item
2622 * is part of
2623 */
2624 public double getEnclosedArea() {
2625 if (_enclosure == null)
2626 return 0.0;
2627 Rectangle2D box = getEnclosedShape().getBounds2D();
2628 return box.getWidth() * box.getHeight();
2629 }
2630
2631 public Rectangle getEnclosedRectangle() {
2632 if (_enclosure == null)
2633 return null;
2634 return getEnclosedShape().getBounds();
2635 }
2636
2637 public int getEnclosureID() {
2638 return _enclosure == null ? 0 : _enclosure.hashCode();
2639 }
2640
2641 /**
2642 * Returns the Shape that surrounds this Item representing this Item's
2643 * 'gravity'.
2644 *
2645 * @return The Shape (rectangle) surrounding this Item, which represents
2646 * this Items 'gravity'.
2647 */
2648 public final Polygon getPolygon() {
2649 if (_poly == null)
2650 updatePolygon();
2651
2652 return new Polygon(_poly.xpoints, _poly.ypoints, _poly.npoints);
2653 }
2654
2655 /**
2656 * Shifts the position of the item along the line between this items
2657 * location and a specified point.
2658 *
2659 * @param origin
2660 * @param ratio
2661 */
2662 public void translate(Point2D origin, double ratio) {
2663
2664 invalidateCommonTraitForAll(ItemAppearence.PreMoved);
2665
2666 _x = (float) (origin.getX() + ratio * (_x - origin.getX()));
2667 _y = (float) (origin.getY() + ratio * (_y - origin.getY()));
2668 updatePolygon();
2669 for (Line line : getLines())
2670 line.updatePolygon();
2671
2672 invalidateCommonTraitForAll(ItemAppearence.PostMoved);
2673
2674 }
2675
2676 private static int[] LinePatterns = new int[] { 0, 10, 20 };
2677
2678 /**
2679 * The rotates through a wheel of dashed lines.
2680 *
2681 * @param amount
2682 * number of rotations around the wheel to toggle by.
2683 */
2684 public void toggleDashed(int amount) {
2685 // find the index of the current line pattern
2686 int[] currentPattern = getLinePattern();
2687
2688 // Find the current pattern and move to the next pattern in the wheel
2689 for (int i = 0; i < LinePatterns.length; i++) {
2690 if (currentPattern == null || currentPattern[0] == LinePatterns[i]) {
2691 i += LinePatterns.length + amount;
2692 i %= LinePatterns.length;
2693
2694 // if we are at the start of the wheel make it 'null' (solid
2695 // line)
2696 if (i == 0) {
2697 setLinePattern(null);
2698 } else {
2699 setLinePattern(new int[] { LinePatterns[i], LinePatterns[i] });
2700 }
2701
2702 invalidateCommonTrait(ItemAppearence.ToggleDashed);
2703 return;
2704 }
2705 }
2706
2707 }
2708
2709 Collection<XRayable> _enclosures = new HashSet<XRayable>();
2710
2711 private boolean _deleted = false;
2712
2713 private Overlay _overlay = null;
2714
2715 protected AttributeValuePair _attributeValuePair = null;
2716
2717 private Float _autoStamp = null;
2718
2719 /**
2720 * For now there can only be one enclosure per item
2721 *
2722 * @param enclosure
2723 */
2724 public void addEnclosure(XRayable enclosure) {
2725 _enclosures.clear();
2726 _enclosures.add(enclosure);
2727 }
2728
2729 /**
2730 * Gets any XRayable items that have this item as a source.
2731 *
2732 * @return the collection of items that are linked to this item as source.
2733 * Guaranteed not to be null.
2734 */
2735 public Collection<? extends XRayable> getEnclosures() {
2736 return _enclosures;
2737 }
2738
2739 public void removeEnclosure(Item i) {
2740 _enclosures.remove(i);
2741
2742 }
2743
2744 public boolean isDeleted() {
2745 return _deleted;
2746 }
2747
2748 /**
2749 * @return The full canvas that this item draws to. Must include
2750 * highlighting bounds
2751 */
2752 public Rectangle[] getDrawingArea() {
2753
2754 return new Rectangle[] { ItemUtils.expandRectangle(getPolygon()
2755 .getBounds(), (int) Math.ceil(Math.max(_highlightThickness,
2756 getThickness()))) };
2757
2758 }
2759
2760 /**
2761 *
2762 * @param area
2763 * @return True if area intersects with this items drawing area.
2764 */
2765 public final boolean isInDrawingArea(Area area) {
2766 for (Rectangle r : getDrawingArea()) {
2767 if (area.intersects(r))
2768 return true;
2769 }
2770 return false;
2771 }
2772
2773 /**
2774 * Completetly invalidates the item - so that it should be redrawed. Note:
2775 * This is handled internally, it should be reare to invoke this externally
2776 */
2777 public final void invalidateAll() {
2778 invalidate(getDrawingArea());
2779 }
2780
2781 /**
2782 * Invalidates areas on the parent frame. Purpose: to be called on specific
2783 * areas of the item that needs redrawing.
2784 *
2785 * @param damagedAreas
2786 */
2787 protected final void invalidate(Rectangle[] damagedAreas) {
2788 for (Rectangle r : damagedAreas)
2789 invalidate(r);
2790 }
2791
2792 /**
2793 * Invalidates areas on the parent frame. Purpose: to be called on specific
2794 * areas of the item that needs redrawing.
2795 *
2796 * @param damagedAreas
2797 */
2798 protected final void invalidate(Rectangle damagedArea) {
2799 FrameGraphics.invalidateItem(this, damagedArea);
2800 }
2801
2802 /**
2803 * Used to invalidate visual traits commonly shared by all items.
2804 *
2805 * @param trait
2806 */
2807 public final void invalidateCommonTrait(ItemAppearence trait) {
2808 invalidate(getDamagedArea(trait));
2809
2810 if (_colorFill != null
2811 && (trait == ItemAppearence.Added || trait == ItemAppearence.Removed)) {
2812 invalidateFill();
2813 }
2814 }
2815
2816 /**
2817 * Invalidates fill if has one, even if no color is set.
2818 */
2819 public void invalidateFill() {
2820 if (isLineEnd() && _enclosure != null) {
2821 invalidate(getEnclosedShape().getBounds());
2822 }
2823 }
2824
2825 /**
2826 * Default implementation always uses drawing area except for links, where
2827 * the link drawing area is used. Override to make item drawing more
2828 * efficient - defining only parts of the item that needs redrawing.
2829 *
2830 * @see Item.getDrawingArea
2831 *
2832 * @param trait
2833 * The visual trait that has changed.
2834 *
2835 * @return The damaged area according to the visual trait that has changed.
2836 */
2837 protected Rectangle[] getDamagedArea(ItemAppearence trait) {
2838
2839 if (trait == ItemAppearence.LinkChanged)
2840 return new Rectangle[] { getLinkDrawArea() }; // Invalidate area
2841 // where link is
2842 // drawn
2843
2844 return getDrawingArea();
2845
2846 }
2847
2848 public boolean hasVector() {
2849 return _overlay instanceof Vector;
2850 }
2851
2852 public boolean hasOverlay() {
2853 return _overlay != null;
2854 }
2855
2856 public Vector getVector() {
2857 if (_overlay instanceof Vector)
2858 return (Vector) _overlay;
2859 return null;
2860 }
2861
2862 public void setOverlay(Overlay overlay) {
2863 _overlay = overlay;
2864 }
2865
2866 public boolean dontSave() {
2867 /*
2868 * TODO Mike says: checkout if the ID check is still needed- When will
2869 * ID still be -1 when saving a frame? assert (i != null);
2870 */
2871 // make it save stuff that's off the screen so stuff isn't deleted by panning - jts21
2872 return !_save || !isVisible() || getID() < 0; // || offScreenTopOrLeft();
2873 }
2874
2875 public void setAnchorLeft(Float anchor) {
2876 this._anchorLeft = anchor;
2877 this._anchorRight = null;
2878 if (anchor != null) {
2879 anchorConstraints();
2880 setX(anchor);
2881 }
2882 }
2883
2884 public void setAnchorRight(Float anchor) {
2885 this._anchorRight = anchor;
2886 this._anchorLeft = null;
2887 if (anchor != null) {
2888 anchorConstraints();
2889 setX(FrameGraphics.getMaxFrameSize().width - anchor
2890 - getBoundsWidth());
2891 }
2892 }
2893
2894 public void setAnchorTop(Float anchor) {
2895 this._anchorTop = anchor;
2896 this._anchorBottom = null;
2897 if (anchor != null) {
2898 anchorConstraints();
2899 setY(anchor);
2900 }
2901 }
2902
2903
2904 public void setAnchorBottom(Float anchor) {
2905 this._anchorBottom = anchor;
2906 this._anchorTop = null;
2907 if (anchor != null) {
2908 anchorConstraints();
2909 setY(FrameGraphics.getMaxFrameSize().height - anchor);
2910 }
2911 }
2912
2913
2914 public boolean isAnchored() {
2915 return ((_anchorLeft != null) || (_anchorRight != null)
2916 || (_anchorTop != null) || (_anchorBottom != null));
2917 }
2918
2919 public boolean isAnchoredX() {
2920 return ((_anchorLeft != null) || (_anchorRight != null));
2921 }
2922
2923 public boolean isAnchoredY() {
2924 return ((_anchorTop != null) || (_anchorBottom != null));
2925 }
2926
2927 public Float getAnchorLeft() {
2928 return _anchorLeft;
2929 }
2930
2931 public Float getAnchorRight() {
2932 return _anchorRight;
2933 }
2934
2935 public Float getAnchorTop() {
2936 return _anchorTop;
2937 }
2938
2939 public Float getAnchorBottom() {
2940 return _anchorBottom;
2941 }
2942
2943 public String getText() {
2944 return "@" + getClass().getSimpleName() + ":" + getID();
2945 }
2946
2947 public void setText(String text) {
2948 }
2949
2950 public boolean recalculateWhenChanged() {
2951 return false;
2952 }
2953
2954 public boolean update() {
2955 return calculate(getText());
2956 }
2957
2958 public Collection<Item> getEnclosedItems() {
2959 return FrameUtils.getItemsEnclosedBy(this.getParentOrCurrentFrame(),
2960 this.getEnclosedShape());
2961 }
2962
2963 public Collection<Text> getEnclosedNonAnnotationText() {
2964 Collection<Text> items = new LinkedHashSet<Text>();
2965 for (Item t : getEnclosedItems()) {
2966 if (t instanceof Text && !t.isAnnotation())
2967 items.add((Text) t);
2968 }
2969
2970 return items;
2971 }
2972
2973 public void dispose() {
2974 setParent(null);
2975 }
2976
2977 /**
2978 * @return
2979 */
2980 protected boolean hasVisibleBorder() {
2981 return getThickness() > 0 && !isLineEnd() && getBorderColor() != null;
2982 }
2983
2984 public Frame getChild() {
2985 String childName = getAbsoluteLink();
2986 if (childName != null) {
2987 return FrameIO.LoadFrame(childName);
2988 }
2989 return null;
2990 }
2991
2992 public boolean hasLink() {
2993 return _link != null;
2994 }
2995
2996 protected void anchorConnectedOLD(AnchorEdgeType anchorEdgeType, Float delta) {
2997 // Check for a more efficient way to do this!!
2998 // Invalidate all the items
2999 for (Item i : this.getAllConnected()) {
3000 i.invalidateAll();
3001 }
3002 // Move the items
3003 for (Item i : this.getAllConnected()) {
3004 if (i.isLineEnd()) {
3005 if (delta != null) {
3006 if ((anchorEdgeType == AnchorEdgeType.Left) || (anchorEdgeType == AnchorEdgeType.Right)) {
3007 // 'delta' encodes a horizontal (x) move
3008 if (anchorEdgeType == AnchorEdgeType.Left) {
3009 i.setAnchorLeft(null);
3010 }
3011 else {
3012 // must be Right
3013 i.setAnchorRight(null);
3014 }
3015
3016 i.setXY(i.getX() + delta, i.getY());
3017 }
3018 if ((anchorEdgeType == AnchorEdgeType.Top) || (anchorEdgeType == AnchorEdgeType.Bottom)) {
3019 // 'delta; encodes a vertical (y) move
3020 if (anchorEdgeType == AnchorEdgeType.Top) {
3021 i.setAnchorTop(null);
3022 }
3023 else {
3024 // must be Bottom
3025 i.setAnchorBottom(null);
3026 }
3027 i.setXY(i.getX(), i.getY() + delta);
3028 }
3029
3030 }
3031 }
3032 }
3033 // Invalidate them again!!
3034 for (Item i : this.getAllConnected()) {
3035 i.updatePolygon();
3036 i.invalidateAll();
3037 }
3038 }
3039
3040
3041 protected void anchorConnected(AnchorEdgeType anchorEdgeType, Float delta) {
3042
3043 // Check for a more efficient way to do this!!
3044 // Invalidate all the items
3045 for (Item i : this.getAllConnected()) {
3046 i.invalidateAll();
3047 }
3048
3049 // Move the items
3050 for (Item i : this.getAllConnected()) {
3051 if (i.isLineEnd()) {
3052 if (delta != null) {
3053 if ((anchorEdgeType == AnchorEdgeType.Left) || (anchorEdgeType == AnchorEdgeType.Right)) {
3054 // 'delta' encodes a horizontal (x) move
3055 if (anchorEdgeType == AnchorEdgeType.Left) {
3056 // Processing a Left anchor
3057 // => Anything connected that is *not* anchored to the right should be moved by 'delta'
3058 if (i.getAnchorRight()==null) {
3059 i.setXY(i.getX() + delta, i.getY());
3060 }
3061 }
3062 else {
3063 // Processing a Right anchor
3064 // => Anything connected that is *not* anchored to the left should be moved by 'delta'
3065 if (i.getAnchorLeft()==null) {
3066 i.setXY(i.getX() + delta, i.getY());
3067 }
3068 }
3069
3070 }
3071 if ((anchorEdgeType == AnchorEdgeType.Top) || (anchorEdgeType == AnchorEdgeType.Bottom)) {
3072 // 'delta; encodes a vertical (y) move
3073 if (anchorEdgeType == AnchorEdgeType.Top) {
3074 // Processing a Top anchor
3075 // => Anything connected that is *not* anchored to the bottom should be moved by 'delta'
3076 if (i.getAnchorBottom()==null) {
3077 i.setXY(i.getX(), i.getY() + delta);
3078 }
3079 }
3080 else {
3081 // Processing a Bottom anchor
3082 // => Anything connected that is *not* anchored to the top should be moved by 'delta'
3083 if (i.getAnchorTop()==null) {
3084 // must be Bottom
3085 //i.setAnchorBottom(null);
3086 i.setXY(i.getX(), i.getY() + delta);
3087 }
3088 }
3089 }
3090 }
3091 }
3092 }
3093
3094 anchorConstraints();
3095
3096 // Invalidate them again!!
3097 for (Item i : this.getAllConnected()) {
3098 i.updatePolygon();
3099 i.invalidateAll();
3100 }
3101 }
3102 /**
3103 * Sets the item to pickup when the user attempts to pick this item up.
3104 * EditTarget has a value of 'this' by default but may be set to other
3105 * values if this item is on a vector.
3106 *
3107 * @param target
3108 * the item to be copied or picked up when the user attempts to
3109 * edit this item.
3110 */
3111 public void setEditTarget(Item target) {
3112 _editTarget = target;
3113 }
3114
3115 /**
3116 * Gets the item to pickup when the user attempts to pick this item up.
3117 * EditTarget has a value of 'this' by default but may be set to other
3118 * values if this item is on a vector.
3119 */
3120 public Item getEditTarget() {
3121 return _editTarget;
3122 }
3123
3124 public void scale(Float scale, int originX, int originY) {
3125 setXY(getX() * scale + originX, getY() * scale + originY);
3126 setArrowheadLength(getArrowheadLength() * scale);
3127
3128 float thickness = getThickness();
3129 if (thickness > 0)
3130 setThickness(thickness * scale, false);
3131
3132 // DONT PUT SIZE IN HERE CAUSE IT STUFFS UP CIRCLES
3133
3134 updatePolygon();
3135 }
3136
3137 protected boolean isVectorItem() {
3138 return _editTarget != this;
3139 }
3140
3141 public AttributeValuePair getAttributeValuePair() {
3142 if (_attributeValuePair == null) {
3143 _attributeValuePair = new AttributeValuePair(getText());
3144 }
3145 return _attributeValuePair;
3146 }
3147
3148 /*
3149 * private static Set<Object> _locks = new HashSet<Object>();
3150 *
3151 * public static void lock(Object itemToLock) { _locks.add(itemToLock); }
3152 *
3153 * public static void unlock(Object itemToUnlock) {
3154 * _locks.remove(itemToUnlock); }
3155 *
3156 * public static boolean isLocked(Object item) { return
3157 * _locks.contains(item); }
3158 */
3159
3160 public void setSave(boolean state) {
3161 _save = state;
3162 }
3163
3164 public boolean getSave() {
3165 return _save;
3166 }
3167
3168 public void setAutoStamp(Float rate) {
3169 _autoStamp = rate;
3170 }
3171
3172 public Float getAutoStamp() {
3173 return _autoStamp;
3174 }
3175
3176 public boolean isAutoStamp() {
3177 return _autoStamp != null && _autoStamp >= 0.0;
3178 }
3179
3180 public void setDotType(DotType type) {
3181 invalidateAll();
3182 _type = type;
3183 invalidateAll();
3184 }
3185
3186 public DotType getDotType() {
3187 return _type;
3188 }
3189
3190 public void setFilled(boolean filled) {
3191 invalidateAll();
3192 _filled = filled;
3193 invalidateAll();
3194 }
3195
3196 public boolean getFilled() {
3197 return _filled;
3198 }
3199}
Note: See TracBrowser for help on using the repository browser.