source: trunk/src/org/expeditee/items/ItemUtils.java@ 589

Last change on this file since 589 was 589, checked in by jts21, 11 years ago

EXA save format working + automatically converting save files to it's own format
Pictures are no longer XRayables, working fine except for a bug with cropping, and some problems with the default image that I need to fix.
FramePicture/FrameImage/FrameBitmap are disabled because they need to be changed to work with the modified Picture format but I don't know what they do so couldn't test them (will need to look into fixing that)

File size: 28.0 KB
Line 
1package org.expeditee.items;
2
3import java.awt.Color;
4import java.awt.Rectangle;
5import java.awt.image.ImageObserver;
6import java.io.File;
7import java.io.UnsupportedEncodingException;
8import java.net.URL;
9import java.net.URLDecoder;
10import java.util.ArrayList;
11import java.util.Collection;
12import java.util.HashMap;
13import java.util.LinkedHashSet;
14import java.util.LinkedList;
15import java.util.List;
16import java.util.Map;
17
18import org.expeditee.gui.DisplayIO;
19import org.expeditee.gui.Frame;
20import org.expeditee.gui.FreeItems;
21import org.expeditee.gui.Vector;
22import org.expeditee.items.Item.HighlightMode;
23import org.expeditee.items.widgets.InteractiveWidget;
24import org.expeditee.items.widgets.InteractiveWidgetInitialisationFailedException;
25import org.expeditee.items.widgets.InteractiveWidgetNotAvailableException;
26import org.expeditee.items.widgets.WidgetCorner;
27import org.expeditee.items.widgets.WidgetEdge;
28
29//Static methods that provide functions for the objects\
30//mostly to transform values (string -> color etc).
31
32/**
33 * Static methods that provide functions for use in Items.
34 */
35public class ItemUtils {
36 // Tag constants
37 public static final int TAG_SORT = 0;
38
39 public static final int TAG_JOIN = 1;
40
41 public static final int TAG_INDENT = 2;
42
43 public static final int TAG_OVERLAY = 3;
44
45 public static final int TAG_ACTIVE_OVERLAY = 4;
46
47 public static final int TAG_IMAGE = 5;
48
49 public static final int TAG_ITEM_TEMPLATE = 6;
50
51 public static final int TAG_ANNOTATION_TEMPLATE = 7;
52
53 public static final int TAG_CODE_COMMENT_TEMPLATE = 8;
54
55 public static final int TAG_MENU = 9;
56
57 public static final int TAG_MENU_NEXT = 10;
58
59 public static final int TAG_PARENT = 11;
60
61 public static final int TAG_LITERAL = 12;
62
63 public static final int TAG_FRAME_IMAGE = 13;
64
65 public static final int TAG_BACKUP = 14;
66
67 public static final int TAG_POINTTYPE = 15;
68
69 // Brook: Im claiming this number!
70 public static final int TAG_IWIDGET = 16;
71
72 public static final int TAG_DOT_TEMPLATE = 17;
73
74 public static final int TAG_STAT_TEMPLATE = 18;
75
76 public static final int TAG_VECTOR = 19;
77
78 public static final int TAG_ACTIVE_VECTOR = 21;
79
80 public static final int TAG_BITMAP_IMAGE = 20;
81
82 public static final int TAG_MIN = 0;
83
84 public static final int TAG_MAX = 21;
85
86 /**
87 * Determines if the given List of Items contains an Item that is one of the
88 * pre-defined tags.
89 *
90 * @param items
91 * The list of Items to search through
92 * @param tag
93 * The Tag to search for, this should correspond to one of the
94 * predefined constants in this class
95 * @return True if an Item was found that is the given Tag, False otherwise.
96 */
97 public static boolean ContainsTag(Collection<Item> items, int tag) {
98 return ContainsTag(items, GetTag(tag));
99 }
100
101 public static boolean ContainsTag(Collection<Item> items, String tag) {
102 return (FindTag(items, tag) != null);
103 }
104
105 public static boolean ContainsExactTag(Collection<Item> items, int tag) {
106 return ContainsExactTag(items, GetTag(tag));
107 }
108
109 public static boolean ContainsExactTag(Collection<Item> items, String tag) {
110 return (FindExactTag(items, tag) != null);
111 }
112
113 /**
114 * Searches the given List of Items for an Item that is one of the
115 * pre-defined tags.
116 *
117 * @param items
118 * The list of Items to search through
119 * @param tag
120 * The Tag to search for, this should correspond to one of the
121 * predefined constants in this class
122 * @return The Item that is the given tag if one is found, or False if none
123 * is found
124 */
125 public static Item FindTag(List<Item> items, int tag) {
126 return FindTag(items, GetTag(tag));
127 }
128
129 /**
130 * Searches the given List of Items for an Item that is the given tag
131 *
132 * @param items
133 * The list of Items to search through
134 * @param toFind
135 * The Tag to search for, this should include the at (@) symbol
136 * @return The Item that is the given tag if one is found, or False if none
137 * is found
138 */
139 public static Text FindTag(Collection<Item> items, String toFind) {
140 for (Item i : items) {
141 if (i instanceof Text && i.isAnnotation())
142 if (((Text) i).startsWith(toFind))
143 return (Text) i;
144 }
145 return null;
146 }
147
148 public static Item FindExactTag(Collection<Item> items, String toFind) {
149 for (Item i : items) {
150 if (i instanceof Text && i.isAnnotation())
151 if (((Text) i).getText().trim().equalsIgnoreCase(toFind))
152 return (Item) i;
153 }
154
155 return null;
156 }
157
158 public static Item FindExactTag(List<Item> items, int tag) {
159 return FindExactTag(items, GetTag(tag));
160 }
161
162 /**
163 * Determines if the given Item is one of the pre-defined tags in this class
164 *
165 * @param toCheck
166 * The Item to check
167 * @param tag
168 * The tag to check the Item against, this should correspond to
169 * one of the constants defined in this class
170 * @return True if the Item matches the given tag, false otherwise
171 */
172 public static boolean startsWithTag(Item toCheck, int tag) {
173 return startsWithTag(toCheck, GetTag(tag));
174 }
175
176 public static boolean startsWithTag(Item toCheck, int tag, boolean hasValue) {
177 return startsWithTag(toCheck, GetTag(tag), hasValue);
178 }
179
180 /**
181 * Checks if the given Item begins with the desired tag (case insensitive).
182 *
183 * @param toCheck
184 * The Item to check for the given tag
185 * @param tag
186 * The tag to check for in the given Item
187 * @param tagOnly
188 * True if the tag does not have a value
189 * @return True if the tag is found in the given Item, False otherwise.
190 */
191 public static boolean startsWithTag(Item toCheck, String tag,
192 boolean valueAllowed) {
193 if (!(toCheck instanceof Text))
194 return false;
195
196 Text txt = (Text) toCheck;
197 String value = ItemUtils.StripTag(txt.getText(), tag);
198
199 if (value == null)
200 return false;
201 return valueAllowed || value.equals("");
202 }
203
204 /**
205 * Checks if the item begins with the desired tag.
206 *
207 * @param toCheck
208 * @param tag
209 * @return
210 */
211 public static boolean startsWithTag(Item toCheck, String tag) {
212 return startsWithTag(toCheck, tag, true);
213 }
214
215 /**
216 * Strips off the given tag from the given String, and returns wathever is
217 * left. Dont put the colon after tags as it is not needed.
218 *
219 * @param toStrip
220 * The String to strip the Tag from
221 * @param tag
222 * The tag to remove from the String
223 * @return The String that results from removing the given Tag from the
224 * given String, or null if the given String is not the given Tag
225 */
226 public static String StripTag(String toStrip, String tag) {
227 if (toStrip == null)
228 return null;
229 toStrip = toStrip.trim();
230 if (!toStrip.toLowerCase().startsWith(tag.toLowerCase()))
231 return null;
232
233 if (toStrip.length() == tag.length())
234 return "";
235 // remove tag and ensure the char is the tag separator
236 char separator = toStrip.charAt(tag.length());
237 if (separator != ':')
238 return null;
239
240 if (toStrip.length() == tag.length() + 1)
241 return "";
242
243 return toStrip.substring(tag.length() + 1).trim();
244 }
245
246 /**
247 * Strips the first character from a string if it is the tag symbol and
248 * returns the remainder.
249 *
250 * @param toStrip
251 * the string to be stripped
252 * @return the stripped version of the string.
253 */
254 public static String StripTagSymbol(String toStrip) {
255 // there must be something left after stripping
256 if (toStrip != null) {
257 toStrip = toStrip.trim();
258 if (toStrip.length() > 0) {
259 if (toStrip.charAt(0) == '@') {
260 return toStrip.substring(1);
261 }
262 }
263 }
264 return toStrip;
265 }
266
267 /**
268 * Converts the given int to the String tag. The int should correspond to
269 * one of the constants in this class, if it does not this method will
270 * return null.
271 *
272 * @param tag
273 * The int corresponding to the constants in this class of which
274 * tag to return
275 * @return The String representation of the given Tag, or null if the given
276 * value does not have a tag associated
277 */
278 public static String GetTag(int tag) {
279 // TODO refactor so that this uses a map for INT to tags
280 switch (tag) {
281 case TAG_SORT:
282 return "@sort";
283 case TAG_JOIN:
284 return "@join";
285 case TAG_INDENT:
286 return "@indent";
287 case TAG_OVERLAY:
288 return "@o";
289 case TAG_VECTOR:
290 return "@v";
291 case TAG_ACTIVE_VECTOR:
292 return "@av";
293 case TAG_ACTIVE_OVERLAY:
294 return "@ao";
295 case TAG_IMAGE:
296 return "@i";
297 case TAG_ITEM_TEMPLATE:
298 return "@itemtemplate";
299 case TAG_ANNOTATION_TEMPLATE:
300 return "@annotationtemplate";
301 case TAG_STAT_TEMPLATE:
302 return "@stattemplate";
303 case TAG_CODE_COMMENT_TEMPLATE:
304 return "@commenttemplate";
305 case TAG_MENU:
306 return "@menu";
307 case TAG_MENU_NEXT:
308 return "@nextmenu";
309 case TAG_PARENT:
310 return "@parent";
311 case TAG_LITERAL:
312 return "@lit";
313 case TAG_FRAME_IMAGE:
314 return "@f";
315 case TAG_BITMAP_IMAGE:
316 return "@b";
317 case TAG_BACKUP:
318 return "@old";
319 case TAG_POINTTYPE:
320 return "@pointtype";
321 case TAG_IWIDGET:
322 return "@iw";
323 case TAG_DOT_TEMPLATE:
324 return "@dottemplate";
325 default:
326 return null;
327 }
328 }
329
330 /**
331 * Creates a picture object from the information stored in the given Text
332 * object. <br>
333 * The paths searched are in the following order:<br>
334 * /images/<br>
335 * the source text as a relative path (from program root folder). <br>
336 * the source text as an absolute path <br>
337 * <br>
338 * If the Image file cannot be found on disk null is returned.
339 *
340 * @param path
341 * The path to the image file
342 * @return The Picture object representing the file, or Null if the file is
343 * not found.
344 */
345 public static Picture CreatePicture(Text source, ImageObserver observer) {
346 String text = source.getText();
347 String path = "";
348 String size = "";
349 String crop = "";
350
351 try {
352 // remove @i tag
353 text = text.replaceFirst("@i:", "");
354 text = text.replaceAll("\n", "");
355 text = text.trim();
356
357 int fileSuffixChar = text.indexOf('.');
358 if (fileSuffixChar < 0)
359 return null;
360 int endOfFileName = text.indexOf(' ', fileSuffixChar);
361 if (endOfFileName < 0) {
362 path = text;
363 size = "";
364 } else {
365 path = text.substring(0, endOfFileName);
366 size = text.substring(endOfFileName).trim();
367 }
368
369 // try images subdirectory
370 File file = null;
371
372 for (String dir : org.expeditee.settings.UserSettings.ImageDirs) {
373 file = new File(dir + path);
374 if (file.exists() && !file.isDirectory())
375 break;
376 }
377
378 if (file == null || !file.exists() || file.isDirectory())
379 file = new File(path);
380
381 // try relative path
382 if (!file.exists() || file.isDirectory()) {
383 URL picture = new Object().getClass().getResource(path);
384
385 // decode to remove %20 in windows folder names
386 if (picture != null) {
387 try {
388 path = URLDecoder.decode(picture.getFile(), "UTF-8");
389 } catch (UnsupportedEncodingException e) {
390 // TODO Auto-generated catch block
391 e.printStackTrace();
392 }
393 }
394
395 } else
396 path = file.getPath();
397
398 // if the image isn't found by now, give up.
399 file = new File(path);
400 if (!file.exists() || file.isDirectory()) {
401 return null;
402 }
403
404 } catch (Exception e) {
405 return null;
406 }
407
408 try {
409 return new Picture(path, size, observer);
410 } catch (Exception e) {
411 e.printStackTrace();
412 return null;
413 }
414
415 }
416
417 /**
418 * Creates a deep copy of the given List of Items.
419 *
420 * @param toCopy
421 * The list of Items to copy
422 * @return A list containing a copy of all Items in the given List
423 */
424 public static List<Item> CopyItems(Collection<Item> toCopy) {
425 return CopyItems(toCopy, false, null);
426 }
427
428 public static List<Item> CopyItems(Collection<Item> toCopy, Vector v) {
429 return CopyItems(toCopy, false, v);
430 }
431
432 public static List<Item> CopyItems(Collection<Item> toCopy, boolean extrude) {
433 return CopyItems(toCopy, extrude, null);
434 }
435
436 public static List<Item> CopyItems(Collection<Item> toCopy,
437 boolean extrude, Vector v) {
438 // The copies to return
439 List<Item> copies = new ArrayList<Item>();
440
441 // list of dots at the end of lines
442 Collection<Item> lineEnds = new LinkedHashSet<Item>();
443 Collection<Line> lines = new LinkedHashSet<Line>();
444 Collection<XRayable> xrayables = new LinkedHashSet<XRayable>();
445 Collection<Constraint> constraints = new LinkedHashSet<Constraint>();
446
447 Collection<Item> singles = new LinkedHashSet<Item>();
448
449 Map<Item, Item> lineEndMap = new HashMap<Item, Item>();
450
451 // Widgets are super special
452 List<InteractiveWidget> widgets = new ArrayList<InteractiveWidget>();
453
454 for (Item i : toCopy) {
455 // Dont copy parts of a vector
456 if (!i.hasPermission(UserAppliedPermission.copy))
457 continue;
458
459 // BROOK
460 if (i instanceof WidgetCorner) { // dont add these
461 if (!widgets.contains(((WidgetCorner) i).getWidgetSource()))
462 widgets.add(((WidgetCorner) i).getWidgetSource());
463 // BROOK
464 } else if (i instanceof WidgetEdge) { // dont add these
465 // lines are recreated later
466 } else if (i instanceof Line) {
467 lines.add((Line) i);
468 } else if (i instanceof XRayable) {
469 xrayables.add((XRayable) i);
470 } else {
471 if (i.isLineEnd()) {
472 lineEnds.add(i);
473 constraints.addAll(i.getConstraints());
474 } else {
475 singles.add(i);
476 }
477 }
478 }
479
480 // Dont copy the other items that are part of the circle
481 for (XRayable x : xrayables) {
482 Collection<Item> connected = x.getConnected();
483 singles.removeAll(connected);
484 lineEnds.removeAll(connected);
485 lines.removeAll(connected);
486 Item xCopy = x.copy();
487 copies.addAll(xCopy.getConnected());
488 // Scale items that are from a vector frame
489 if (v != null) {
490 scaleItem(v, xCopy);
491 }
492 }
493
494 // copy all single items
495 for (Item i : singles) {
496 Item copy = i.copy();
497 Frame parent = i.getParent();
498 if (parent != null) {
499 // Items copied from overlay will be anchored onto the current
500 // frame
501 copy.setParent(parent);
502 // if this is the frame name, make sure the frame is saved (in
503 // case it is a TDFC frame)
504 if (i.isFrameName()) {
505 parent.setChanged(true);
506 }// check if the item is being copied from a vector
507 else if (v != null) {
508 // Find the vector this item is from
509 assert (v.Frame == parent);
510 scaleItem(v, copy);
511 }
512 }
513 copies.add(copy);
514 }
515
516 // replace line ends with their copies
517 // this is done here so that copied lines can still share end points
518 for (Item i : lineEnds) {
519 // create a copy of the line end
520 Item copy = i.copy();
521 copy.removeAllLines();
522 copy.removeAllConstraints();
523
524 if (extrude) {
525 Frame frame = i.getParentOrCurrentFrame();
526 Line newLine = new Line(i, copy, frame.getNextItemID());
527 // make sure overlay items are put back on the overlay
528 newLine.setParent(frame);
529 frame.addItem(newLine);
530 copies.add(newLine);
531 }
532 copies.add(copy);
533 lineEndMap.put(i, copy);
534 // Scale items that are from a vector frame
535 if (v != null) {
536 scaleItem(v, copy);
537 }
538 }
539
540 // recreate lines
541 for (Line line : lines) {
542 Line lineCopy = line.copy();
543 // get the lineEnd we copied above if it is in the MAPPING
544 Item originalLineEnd = line.getEndItem();
545 Item actualLineEnd = lineEndMap.get(originalLineEnd);
546 if (actualLineEnd == null)
547 lineCopy.setEndItem(originalLineEnd);
548 else
549 lineCopy.setEndItem(actualLineEnd);
550
551 Item originalLineStart = line.getStartItem();
552 Item actualLineStart = lineEndMap.get(originalLineStart);
553 if (actualLineStart == null)
554 lineCopy.setStartItem(originalLineStart);
555 else
556 lineCopy.setStartItem(actualLineStart);
557
558 copies.add(lineCopy);
559 }
560
561 // recreate constraints
562 for (Constraint c : constraints) {
563 Item start = lineEndMap.get(c.getStart());
564 Item end = lineEndMap.get(c.getEnd());
565 int id = DisplayIO.getCurrentFrame().getNextItemID();
566 if (start != null && end != null) {
567 new Constraint(start, end, id, c.getType());
568 }
569 }
570
571 // BROOK
572 for (InteractiveWidget iw : widgets) {
573 try {
574
575 InteractiveWidget icopy = iw.copy();
576 copies.addAll(icopy.getItems());
577
578 } catch (InteractiveWidgetNotAvailableException e) {
579 e.printStackTrace();
580 } catch (InteractiveWidgetInitialisationFailedException e) {
581 e.printStackTrace();
582 }
583
584 }
585
586 // Make sure filled rectangles are shown filled on vector overlays
587 if (v != null)
588 EnclosedCheck(copies);
589
590 return copies;
591 }
592
593 /**
594 * Attempts to create a new line that starts from the given Item
595 * ('unreeling'). The Item must already have at least one line, and not be a
596 * line itself to be unreeled from.
597 *
598 * @param toUnreelFrom
599 * The Item that will be one end point of the new line
600 * @return A List containing the newly created Item and Line that unreel
601 * from the given Item, or null if this Item cannot be unreeled
602 * from.
603 */
604 public static List<Item> UnreelLine(Item toUnreelFrom, boolean constrain) {
605 // the Item must already have one line to be unreeled from
606 if (toUnreelFrom == null || toUnreelFrom.getLines().size() < 1)
607 return null;
608
609 List<Item> unreel = new ArrayList<Item>(2);
610 unreel.add(toUnreelFrom);
611 unreel.addAll(toUnreelFrom.getLines());
612 return UnreelLine(unreel, constrain);
613 }
614
615 /**
616 * Attempts to create a new line that starts from the given list of Items
617 * ('unreeling'). The List must contain only one non-line Item. The non-line
618 * Item must already have at least one line to be unreeled from.
619 *
620 * @param toUnreel
621 * The List containing the Item that will be one end point of the
622 * new line
623 * @return A List of the newly created Item and Line that unreel from the
624 * Item in the given List, or null if this List cannot be unreeled
625 * from.
626 */
627 public static List<Item> UnreelLine(List<Item> toUnreel, boolean constrain) {
628 Item origEnd = null;
629 // find the end being unreeled from
630 for (Item item : toUnreel) {
631 // we dont want to unreel anything other than lines
632 if (item.hasEnclosures()
633 || !(item.isLineEnd() || item instanceof Line)) {
634 return null;
635 }
636 // find the dot to unreel from
637 if (item.isLineEnd()) {
638 // if there are multiple ends in the list, return
639 if (origEnd != null)
640 return null;
641
642 origEnd = item;
643 }
644 }
645
646 // copy the original endpoint
647 Item copy = origEnd.copy();
648 origEnd.setHighlightMode(HighlightMode.None);
649 copy.removeAllLines();
650 copy.removeAllConstraints();
651
652 for (Line l : origEnd.getLines()) {
653 l.invalidateAll();
654 }
655
656 // create a new line
657 Frame currentFrame = DisplayIO.getCurrentFrame();
658 Line line = new Line(origEnd, copy, currentFrame.getNextItemID());
659 // if the previous line was constrained then make the new line
660 // constrained if it was a single line
661 // TODO add later a diagonal constraint if getLines() == 3 or 4
662 Collection<Constraint> constraints = origEnd.getConstraints();
663 if (constrain && constraints.size() > 0
664 && origEnd.getLines().size() == 2) {
665 Integer type = null;
666 for (Constraint c : constraints) {
667 if (c.getType() == Constraint.HORIZONTAL) {
668 type = Constraint.VERTICAL;
669 } else if (c.getType() == Constraint.VERTICAL) {
670 type = Constraint.HORIZONTAL;
671 }
672 if (c.getType() == Constraint.DIAGONAL_NEG) {
673 type = Constraint.DIAGONAL_POS;
674 } else if (c.getType() == Constraint.DIAGONAL_POS) {
675 type = Constraint.DIAGONAL_NEG;
676 }
677 }
678 if (type != null) {
679 new Constraint(origEnd, copy, currentFrame.getNextItemID(),
680 type);
681 }
682 }
683
684 // copy.setFloating(true);
685 origEnd.setArrowheadLength(0);
686 // copy.setArrowheadLength(0);
687
688 List<Item> toReturn = new LinkedList<Item>();
689 toReturn.add(copy);
690 toReturn.add(line);
691 return toReturn;
692 }
693
694 public static void New() {
695 EnclosedCheck(DisplayIO.getCurrentFrame().getItems());
696 }
697
698 public static void Old() {
699 OldEnclosedCheck(DisplayIO.getCurrentFrame().getItems());
700 }
701
702 /**
703 * Updates the connectedToAnnotation flags for all items
704 */
705 public static void UpdateConnectedToAnnotations(Collection<Item> items) {
706 // get all lineEnds on the Frame
707 Collection<Item> lineEnds = new LinkedHashSet<Item>();
708 for (Item i : items) {
709 i.setConnectedToAnnotation(false);
710 if (i.isLineEnd()) {
711 lineEnds.add(i);
712 }
713 }
714
715 // if there are no line endpoints on the Frame, then there can't be an
716 // enclosure
717 if (lineEnds.size() == 0)
718 return;
719
720 // Now find go through line ends and see if any are annotation items
721 while (lineEnds.size() > 0) {
722 Item item = lineEnds.iterator().next();
723 // If its an annotation item then set the flag for all its connected
724 // items
725 if (item.isAnnotation()) {
726 Collection<Item> connected = item.getAllConnected();
727 for (Item i : connected)
728 i.setConnectedToAnnotation(true);
729 lineEnds.removeAll(connected);
730 }
731 lineEnds.remove(item);
732 }
733 }
734
735 /**
736 * Checks through all Lines and Dots on the current Frame to detect if any
737 * form an enclosure, which can then be used to manipulate items within the
738 * polygon. If an enclosure is found, then the dots will have their
739 * enclosure value set to true, and a List is created that contains all the
740 * Dots in the order they were processed. Actual calculation of the Polygon
741 * is done dynamically (to account for Dots being moved).
742 */
743 public static void EnclosedCheck(Collection<Item> items) {
744 // get all lineEnds on the Frame
745 List<Item> lineEnds = new LinkedList<Item>();
746 for (Item i : items) {
747 if (i.isLineEnd()) {
748 i.setEnclosedList(null);
749 // Add line ends joined to 2 other lines
750 if (i.getLines().size() == 2)
751 lineEnds.add(i);
752 }
753 }
754
755 // if there are no line endpoints on the Frame, then there can't be an
756 // enclosure
757 if (lineEnds.size() == 0)
758 return;
759
760 // New approach
761 while (lineEnds.size() > 0) {
762 Item item = lineEnds.get(0);
763 // Get the lineEnds connected to this item
764 Collection<Item> connected = item.getAllConnected();
765 Collection<Item> connectedLineEnds = new LinkedHashSet<Item>();
766 for (Item itemToCheck : connected) {
767 if (itemToCheck.isLineEnd())
768 connectedLineEnds.add(itemToCheck);
769 }
770 // Check that all the line ends are in our lineEnds list
771 int oldSize = lineEnds.size();
772 // Remove all the items from our line ends list
773 lineEnds.removeAll(connectedLineEnds);
774 int newSize = lineEnds.size();
775 int connectedSize = connectedLineEnds.size();
776 // Check if all the connectedItems were in the lineEnds collection
777 if (oldSize == newSize + connectedSize) {
778 // Set them to be the enclosed list for each of the items
779 for (Item enclosedLineEnd : connectedLineEnds) {
780 enclosedLineEnd.setEnclosedList(connectedLineEnds);
781 }
782 }
783 }
784 }
785
786 /**
787 * Checks through all Lines and Dots on the current Frame to detect if any
788 * form an enclosure, which can then be used to manipulate items within the
789 * polygon. If an enclosure is found, then the dots will have their
790 * enclosure value set to true, and a List is created that contains all the
791 * Dots in the order they were processed. Actual calculation of the Polygon
792 * is done dynamically (to account for Dots being moved).
793 */
794 public static void OldEnclosedCheck(Collection<Item> items) {
795 _seen.clear();
796
797 // get all lineEnds on the Frame
798 List<Item> lineEnds = new ArrayList<Item>(0);
799 for (Item i : items) {
800 if (i.isLineEnd()) {
801 i.setEnclosedList(null);
802
803 if (i.getLines().size() == 2)
804 lineEnds.add(i);
805 }
806 }
807
808 // if there are no line endpoints on the Frame, then there can't be an
809 // enclosure
810 if (lineEnds.size() == 0)
811 return;
812
813 // TODO optimise this code!!
814 // iterate through all the lineEnds
815 for (Item searchFor : lineEnds) {
816 _seen.clear();
817
818 for (Line l : searchFor.getLines()) {
819 _seen.add(l);
820 if (traverse(searchFor, l.getOppositeEnd(searchFor))) {
821 _path.add(l.getOppositeEnd(searchFor));
822
823 for (Item i : _path)
824 i.setEnclosedList(_path);
825
826 _path = new ArrayList<Item>(0);
827
828 break;
829 }
830 }
831 }
832 }
833
834 private static List<Line> _seen = new ArrayList<Line>();
835
836 private static List<Item> _path = new ArrayList<Item>();
837
838 private static boolean traverse(Item toFind, Item searchFrom) {
839 if (toFind == null || searchFrom == null || !searchFrom.isLineEnd())
840 return false;
841
842 if (searchFrom.getLines().size() != 2)
843 return false;
844
845 if (toFind == searchFrom)
846 return true;
847
848 for (Line l : searchFrom.getLines()) {
849 if (!(_seen.contains(l))) {
850 _seen.add(l);
851 if (traverse(toFind, l.getOppositeEnd(searchFrom))) {
852 _path.add(l.getOppositeEnd(searchFrom));
853 return true;
854 }
855 }
856
857 }
858
859 return false;
860 }
861
862 /**
863 * Determines if an item is visible from a the current frame(s). If the item
864 * is free then it is considered visible.
865 *
866 * @param i
867 * The item to check
868 * @return True if visible/free from given frame.
869 */
870 public static boolean isVisible(Item i) {
871 if (DisplayIO.isTwinFramesOn()) {
872 if (!isVisible(DisplayIO.getFrames()[0], i)) {
873 return isVisible(DisplayIO.getFrames()[1], i);
874 } else {
875 return true;
876 }
877 } else {
878 return isVisible(DisplayIO.getCurrentFrame(), i);
879 }
880 }
881
882 /**
883 * Determines if an item is visible from a given frame. If the item is free
884 * then it is considered visible.
885 *
886 * @param fromFrame
887 * The frame to check from.
888 * @param i
889 * The item to check
890 * @return True if visible/free from given frame.
891 */
892 public static boolean isVisible(Frame fromFrame, Item i) {
893 if (fromFrame == null)
894 return false;
895
896 Frame parent = i.getParent();
897
898 if (parent == fromFrame)
899 return true;
900
901 else if (parent == null)
902 return FreeItems.getInstance().contains(i)
903 || FreeItems.getCursor().contains(i);
904
905 return fromFrame.getAllItems().contains(i) && i.isVisible();
906 }
907
908 public static Rectangle expandRectangle(Rectangle r, int n) {
909 return new Rectangle(r.x - (n >> 1), r.y - (n >> 1), r.width + n,
910 r.height + n);
911 }
912
913 /*
914 * FrameMouseActions while (!copies.isEmpty()) { Iterator<Item> iterator =
915 * copies.iterator(); Item item = iterator.next(); // Dont paint annotation
916 * items for @v if (!item.isVisible() || item.isAnnotation()) {
917 * iterator.remove(); continue; }
918 *
919 * if (!(item instanceof Line)) { item.setThickness(item.getThickness() *
920 * scale); if (item instanceof XRayable || item.hasEnclosures()) {
921 * scaleItem(scale, defaultForeground, defaultBackground, origin.x,
922 * origin.y, item); items.add(item); copies.remove(item); } else {
923 * Collection<Item> connected = item.getAllConnected(); // Get all the
924 * connected items because we can only set the // thickness ONCE for (Item i :
925 * connected) { scaleItem(scale, defaultForeground, defaultBackground,
926 * origin.x, origin.y, i); } items.addAll(connected);
927 * copies.removeAll(connected); } } else { iterator.remove(); } }
928 */
929
930 /**
931 * @param scale
932 * @param defaultForeground
933 * @param defaultBackground
934 * @param originX
935 * @param originY
936 * @param item
937 */
938 private static void scaleItem(Vector v, Item item) {
939 Float scale = v.Scale;
940 int originX = v.Origin.x;
941 int originY = v.Origin.y;
942 Color defaultForeground = v.Foreground;
943 Color defaultBackground = v.Background;
944 UserAppliedPermission permission = v.permission;
945 item.setPermission(permission);
946
947 // TODO encapsulate this somewhere inside of circle class!
948 // if(item instanceof Circle){
949 // scaleItem(v, ((Circle)item).getCenter());
950 // }
951
952 if (!(item instanceof Line)) {
953 if (item.getColor() == null) {
954 item.setColor(defaultForeground);
955 }
956 if (item.getBackgroundColor() == null) {
957 item.setBackgroundColor(defaultBackground);
958 }
959 if (item.getFillColor() == null) {
960 item.setFillColor(defaultBackground);
961 }
962
963 if (permission.equals(UserAppliedPermission.none)) {
964 item.setLinkMark(false);
965 item.setActionMark(false);
966 }
967
968 item.scale(scale, originX, originY);
969 }
970 }
971
972 /**
973 * Extracts widgets from an item list.
974 *
975 * @param items
976 * Items to extract from. Must not be null.
977 *
978 * @return List of (unique)widgets in items. Never null.
979 */
980 public static List<InteractiveWidget> extractWidgets(List<Item> items) {
981 assert (items != null);
982
983 List<InteractiveWidget> iWidgets = new LinkedList<InteractiveWidget>();
984
985 for (Item i : items) {
986 if (i instanceof WidgetEdge) {
987 WidgetEdge we = (WidgetEdge) i;
988 if (!iWidgets.contains(we.getWidgetSource()))
989 iWidgets.add(we.getWidgetSource());
990 } else if (i instanceof WidgetCorner) {
991 WidgetCorner wc = (WidgetCorner) i;
992 if (!iWidgets.contains(wc.getWidgetSource()))
993 iWidgets.add(wc.getWidgetSource());
994 }
995 }
996
997 return iWidgets;
998 }
999}
Note: See TracBrowser for help on using the repository browser.