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

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

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

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