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

Last change on this file since 940 was 940, checked in by bln4, 9 years ago

Continued work on update to tooltips to be actual items so that data can be stored.

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