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

Last change on this file since 1426 was 1426, checked in by bln4, 5 years ago

Implemented surrogates for images. When you add an encryption label to a picture, the default is a on-the-fly generated image of noise. This generated image has the same size specifications as the primary image.

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