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

Last change on this file since 1245 was 1245, checked in by davidb, 5 years ago

ImageDirs and AudioDirs changed over to new DirectcoryListSetting; further work done on title/framename position to help startup/init code

File size: 30.7 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 // try images subdirectory
404 File file = null;
405
406 for (String dir : FolderSettings.ImageDirs.getAbsoluteDirs()) {
407 file = new File(dir + path);
408 if (file.exists() && !file.isDirectory()) {
409 break;
410 }
411 }
412
413 if (file == null || !file.exists() || file.isDirectory()) {
414 file = new File(path);
415 }
416
417 // try relative path
418 if (!file.exists() || file.isDirectory()) {
419 URL picture = new Object().getClass().getResource(path);
420
421 // decode to remove %20 in windows folder names
422 if (picture != null) {
423 try {
424 path = URLDecoder.decode(picture.getFile(), "UTF-8");
425 } catch (UnsupportedEncodingException e) {
426 // TODO Auto-generated catch block
427 e.printStackTrace();
428 }
429 }
430
431 } else {
432 path = file.getPath();
433 }
434
435 // if the image isn't found by now, try remote servers
436 file = new File(path);
437 if (!file.exists() || file.isDirectory()) {
438 if(tryRemote && FrameShare.getInstance().loadImage(fileName, null)) {
439 // call CreatePicture again, but with tryRemote set to false so we won't get into an infinite loop
440 // if something goes wrong with finding the downloaded image
441 return CreatePicture(source, false);
442 }
443 return null;
444 }
445
446 } catch (Exception e) {
447 return null;
448 }
449
450 try {
451 Picture pic = new Picture(source, fileName, path, size);
452
453 return pic;
454 } catch (Exception e) {
455 e.printStackTrace();
456 return null;
457 }
458
459 }
460
461 public static Picture CreatePicture(Text source) {
462 return CreatePicture(source, true);
463 }
464
465 /**
466 * Creates a deep copy of the given List of Items.
467 *
468 * @param toCopy
469 * The list of Items to copy
470 * @return A list containing a copy of all Items in the given List
471 */
472 public static List<Item> CopyItems(Collection<Item> toCopy) {
473 return CopyItems(toCopy, false, null);
474 }
475
476 public static List<Item> CopyItems(Collection<Item> toCopy, Vector v) {
477 return CopyItems(toCopy, false, v);
478 }
479
480 public static List<Item> CopyItems(Collection<Item> toCopy, boolean extrude) {
481 return CopyItems(toCopy, extrude, null);
482 }
483
484 public static List<Item> CopyItems(Collection<Item> toCopy,
485 boolean extrude, Vector v) {
486 // The copies to return
487 List<Item> copies = new ArrayList<Item>();
488
489 // list of dots at the end of lines
490 Collection<Item> lineEnds = new LinkedHashSet<Item>();
491 Collection<Line> lines = new LinkedHashSet<Line>();
492 Collection<XRayable> xrayables = new LinkedHashSet<XRayable>();
493 Collection<Constraint> constraints = new LinkedHashSet<Constraint>();
494
495 Collection<Item> singles = new LinkedHashSet<Item>();
496
497 Map<Item, Item> lineEndMap = new HashMap<Item, Item>();
498
499 // Widgets are super special
500 List<Widget> widgets = new ArrayList<Widget>();
501
502 for (Item i : toCopy) {
503 // Dont copy parts of a vector
504 if (i == null || !i.hasPermission(UserAppliedPermission.copy)) {
505 continue;
506 }
507
508 // BROOK
509 if (i instanceof WidgetCorner) { // dont add these
510 if (!widgets.contains(((WidgetCorner) i).getWidgetSource()))
511 {
512 widgets.add(((WidgetCorner) i).getWidgetSource());
513 // BROOK
514 }
515 } else if (i instanceof WidgetEdge) { // dont add these
516 // lines are recreated later
517 } else if (i instanceof Line) {
518 lines.add((Line) i);
519 } else if (i instanceof XRayable) {
520 xrayables.add((XRayable) i);
521 } else {
522 if (i.isLineEnd()) {
523 lineEnds.add(i);
524 constraints.addAll(i.getConstraints());
525 } else {
526 singles.add(i);
527 }
528 }
529 }
530
531 // Dont copy the other items that are part of the circle
532 for (XRayable x : xrayables) {
533 Collection<Item> connected = x.getConnected();
534 singles.removeAll(connected);
535 lineEnds.removeAll(connected);
536 lines.removeAll(connected);
537 Item xCopy = x.copy();
538 copies.addAll(xCopy.getConnected());
539 // Scale items that are from a vector frame
540 if (v != null) {
541 scaleItem(v, xCopy);
542 }
543 }
544
545 // copy all single items
546 for (Item i : singles) {
547 Item copy = i.copy();
548 Frame parent = i.getParent();
549 if (parent != null) {
550 // Items copied from overlay will be anchored onto the current
551 // frame
552 copy.setParent(parent);
553 // if this is the frame name, make sure the frame is saved (in
554 // case it is a TDFC frame)
555 if (i.isFrameName()) {
556 parent.setChanged(true);
557 }// check if the item is being copied from a vector
558 else if (v != null) {
559 // Find the vector this item is from
560 assert (v.Frame == parent);
561 scaleItem(v, copy);
562 }
563 }
564 copies.add(copy);
565 }
566
567 // replace line ends with their copies
568 // this is done here so that copied lines can still share end points
569 for (Item i : lineEnds) {
570 // create a copy of the line end
571 Item copy = i.copy();
572 copy.removeAllLines();
573 copy.removeAllConstraints();
574
575 if (extrude) {
576 Frame frame = i.getParentOrCurrentFrame();
577 Line newLine = new Line(i, copy, frame.getNextItemID());
578 // make sure overlay items are put back on the overlay
579 newLine.setParent(frame);
580 frame.addItem(newLine);
581 copies.add(newLine);
582 }
583 copies.add(copy);
584 lineEndMap.put(i, copy);
585 // Scale items that are from a vector frame
586 if (v != null) {
587 scaleItem(v, copy);
588 }
589 }
590
591 // recreate lines
592 for (Line line : lines) {
593 Line lineCopy = line.copy();
594 // get the lineEnd we copied above if it is in the MAPPING
595 Item originalLineEnd = line.getEndItem();
596 Item actualLineEnd = lineEndMap.get(originalLineEnd);
597 if (actualLineEnd == null) {
598 lineCopy.setEndItem(originalLineEnd);
599 } else {
600 lineCopy.setEndItem(actualLineEnd);
601 }
602
603 Item originalLineStart = line.getStartItem();
604 Item actualLineStart = lineEndMap.get(originalLineStart);
605 if (actualLineStart == null) {
606 lineCopy.setStartItem(originalLineStart);
607 } else {
608 lineCopy.setStartItem(actualLineStart);
609 }
610
611 copies.add(lineCopy);
612 }
613
614 // recreate constraints
615 for (Constraint c : constraints) {
616 Item start = lineEndMap.get(c.getStart());
617 Item end = lineEndMap.get(c.getEnd());
618 int id = DisplayController.getCurrentFrame().getNextItemID();
619 if (start != null && end != null) {
620 new Constraint(start, end, id, c.getType());
621 }
622 }
623
624 // BROOK
625 for (Widget iw : widgets) {
626 try {
627
628 Widget icopy = iw.copy();
629 copies.addAll(icopy.getItems());
630
631 } catch (InteractiveWidgetNotAvailableException e) {
632 e.printStackTrace();
633 } catch (InteractiveWidgetInitialisationFailedException e) {
634 e.printStackTrace();
635 }
636
637 }
638
639 // Make sure filled rectangles are shown filled on vector overlays
640 if (v != null) {
641 EnclosedCheck(copies);
642 }
643
644 return copies;
645 }
646
647 /**
648 * Attempts to create a new line that starts from the given Item
649 * ('unreeling'). The Item must already have at least one line, and not be a
650 * line itself to be unreeled from.
651 *
652 * @param toUnreelFrom
653 * The Item that will be one end point of the new line
654 * @return A List containing the newly created Item and Line that unreel
655 * from the given Item, or null if this Item cannot be unreeled
656 * from.
657 */
658 public static List<Item> UnreelLine(Item toUnreelFrom, boolean constrain) {
659 // the Item must already have one line to be unreeled from
660 if (toUnreelFrom == null || toUnreelFrom.getLines().size() < 1) {
661 return null;
662 }
663
664 List<Item> unreel = new ArrayList<Item>(2);
665 unreel.add(toUnreelFrom);
666 unreel.addAll(toUnreelFrom.getLines());
667 return UnreelLine(unreel, constrain);
668 }
669
670 /**
671 * Attempts to create a new line that starts from the given list of Items
672 * ('unreeling'). The List must contain only one non-line Item. The non-line
673 * Item must already have at least one line to be unreeled from.
674 *
675 * @param toUnreel
676 * The List containing the Item that will be one end point of the
677 * new line
678 * @return A List of the newly created Item and Line that unreel from the
679 * Item in the given List, or null if this List cannot be unreeled
680 * from.
681 */
682 public static List<Item> UnreelLine(List<Item> toUnreel, boolean constrain) {
683 Item origEnd = null;
684 // find the end being unreeled from
685 for (Item item : toUnreel) {
686 // we dont want to unreel anything other than lines
687 if (item.hasEnclosures()
688 || !(item.isLineEnd() || item instanceof Line)) {
689 return null;
690 }
691 // find the dot to unreel from
692 if (item.isLineEnd()) {
693 // if there are multiple ends in the list, return
694 if (origEnd != null) {
695 return null;
696 }
697
698 origEnd = item;
699 }
700 }
701
702 // copy the original endpoint
703 Item copy = origEnd.copy();
704 origEnd.setHighlightMode(HighlightMode.None);
705 origEnd.setHighlightColorToDefault();
706 copy.removeAllLines();
707 copy.removeAllConstraints();
708
709 for (Line l : origEnd.getLines()) {
710 l.invalidateAll();
711 }
712
713 // create a new line
714 Frame currentFrame = DisplayController.getCurrentFrame();
715 Line line = new Line(origEnd, copy, currentFrame.getNextItemID());
716 // if the previous line was constrained then make the new line
717 // constrained if it was a single line
718 // TODO add later a diagonal constraint if getLines() == 3 or 4
719 Collection<Constraint> constraints = origEnd.getConstraints();
720 if (constrain && constraints.size() > 0 && origEnd.getLines().size() == 2) {
721 Integer type = null;
722 for (Constraint c : constraints) {
723 if (c.getType() == Constraint.HORIZONTAL) {
724 type = Constraint.VERTICAL;
725 } else if (c.getType() == Constraint.VERTICAL) {
726 type = Constraint.HORIZONTAL;
727 }
728 if (c.getType() == Constraint.DIAGONAL_NEG) {
729 type = Constraint.DIAGONAL_POS;
730 } else if (c.getType() == Constraint.DIAGONAL_POS) {
731 type = Constraint.DIAGONAL_NEG;
732 }
733 }
734 if (type != null) {
735 new Constraint(origEnd, copy, currentFrame.getNextItemID(), type);
736 }
737 }
738
739 // copy.setFloating(true);
740 origEnd.setArrowheadLength(0);
741 // copy.setArrowheadLength(0);
742
743 List<Item> toReturn = new LinkedList<Item>();
744 toReturn.add(copy);
745 toReturn.add(line);
746 return toReturn;
747 }
748
749 public static void New() {
750 EnclosedCheck(DisplayController.getCurrentFrame().getItems());
751 }
752
753 public static void Old() {
754 OldEnclosedCheck(DisplayController.getCurrentFrame().getItems());
755 }
756
757 /**
758 * Updates the connectedToAnnotation flags for all items
759 */
760 public static void UpdateConnectedToAnnotations(Collection<Item> items) {
761 // get all lineEnds on the Frame
762 Collection<Item> lineEnds = new LinkedHashSet<Item>();
763 for (Item i : items) {
764 i.setConnectedToAnnotation(false);
765 if (i.isLineEnd()) {
766 lineEnds.add(i);
767 }
768 }
769
770 // if there are no line endpoints on the Frame, then there can't be an
771 // enclosure
772 if (lineEnds.size() == 0) {
773 return;
774 }
775
776 // Now find go through line ends and see if any are annotation items
777 while (lineEnds.size() > 0) {
778 Item item = lineEnds.iterator().next();
779 // If its an annotation item then set the flag for all its connected
780 // items
781 if (item.isAnnotation()) {
782 Collection<Item> connected = item.getAllConnected();
783 for (Item i : connected) {
784 i.setConnectedToAnnotation(true);
785 }
786 lineEnds.removeAll(connected);
787 }
788 lineEnds.remove(item);
789 }
790 }
791
792 /**
793 * Checks through all Lines and Dots on the current Frame to detect if any
794 * form an enclosure, which can then be used to manipulate items within the
795 * polygon. If an enclosure is found, then the dots will have their
796 * enclosure value set to true, and a List is created that contains all the
797 * Dots in the order they were processed. Actual calculation of the Polygon
798 * is done dynamically (to account for Dots being moved).
799 */
800 public static void EnclosedCheck(Collection<Item> items) {
801 // get all lineEnds on the Frame
802 List<Item> lineEnds = new LinkedList<Item>();
803 for (Item i : items) {
804 if (i.isLineEnd()) {
805 i.setEnclosedList(null);
806 // Add line ends joined to 2 other lines
807 if (i.getLines().size() == 2) {
808 lineEnds.add(i);
809 }
810 }
811 }
812
813 // if there are no line endpoints on the Frame, then there can't be an
814 // enclosure
815 if (lineEnds.size() == 0) {
816 return;
817 }
818
819 // New approach
820 while (lineEnds.size() > 0) {
821 Item item = lineEnds.get(0);
822 // Get the lineEnds connected to this item
823 Collection<Item> connected = item.getAllConnected();
824 Collection<Item> connectedLineEnds = new LinkedHashSet<Item>();
825 for (Item itemToCheck : connected) {
826 if (itemToCheck.isLineEnd()) {
827 connectedLineEnds.add(itemToCheck);
828 }
829 }
830 // Check that all the line ends are in our lineEnds list
831 int oldSize = lineEnds.size();
832 // Remove all the items from our line ends list
833 lineEnds.removeAll(connectedLineEnds);
834 int newSize = lineEnds.size();
835 int connectedSize = connectedLineEnds.size();
836 // Check if all the connectedItems were in the lineEnds collection
837 if (oldSize == newSize + connectedSize) {
838 // Set them to be the enclosed list for each of the items
839 for (Item enclosedLineEnd : connectedLineEnds) {
840 enclosedLineEnd.setEnclosedList(connectedLineEnds);
841 }
842 }
843 }
844 }
845
846 /**
847 * Checks through all Lines and Dots on the current Frame to detect if any
848 * form an enclosure, which can then be used to manipulate items within the
849 * polygon. If an enclosure is found, then the dots will have their
850 * enclosure value set to true, and a List is created that contains all the
851 * Dots in the order they were processed. Actual calculation of the Polygon
852 * is done dynamically (to account for Dots being moved).
853 */
854 public static void OldEnclosedCheck(Collection<Item> items) {
855 _seen.clear();
856
857 // get all lineEnds on the Frame
858 List<Item> lineEnds = new ArrayList<Item>(0);
859 for (Item i : items) {
860 if (i.isLineEnd()) {
861 i.setEnclosedList(null);
862
863 if (i.getLines().size() == 2) {
864 lineEnds.add(i);
865 }
866 }
867 }
868
869 // if there are no line endpoints on the Frame, then there can't be an
870 // enclosure
871 if (lineEnds.size() == 0) {
872 return;
873 }
874
875 // TODO optimise this code!!
876 // iterate through all the lineEnds
877 for (Item searchFor : lineEnds) {
878 _seen.clear();
879
880 for (Line l : searchFor.getLines()) {
881 _seen.add(l);
882 if (traverse(searchFor, l.getOppositeEnd(searchFor))) {
883 _path.add(l.getOppositeEnd(searchFor));
884
885 for (Item i : _path) {
886 i.setEnclosedList(_path);
887 }
888
889 _path = new ArrayList<Item>(0);
890
891 break;
892 }
893 }
894 }
895 }
896
897 private static List<Line> _seen = new ArrayList<Line>();
898
899 private static List<Item> _path = new ArrayList<Item>();
900
901 private static boolean traverse(Item toFind, Item searchFrom) {
902 if (toFind == null || searchFrom == null || !searchFrom.isLineEnd()) {
903 return false;
904 }
905
906 if (searchFrom.getLines().size() != 2) {
907 return false;
908 }
909
910 if (toFind == searchFrom) {
911 return true;
912 }
913
914 for (Line l : searchFrom.getLines()) {
915 if (!(_seen.contains(l))) {
916 _seen.add(l);
917 if (traverse(toFind, l.getOppositeEnd(searchFrom))) {
918 _path.add(l.getOppositeEnd(searchFrom));
919 return true;
920 }
921 }
922
923 }
924
925 return false;
926 }
927
928 /**
929 * Determines if an item is visible from a the current frame(s). If the item
930 * is free then it is considered visible.
931 *
932 * @param i
933 * The item to check
934 * @return True if visible/free from given frame.
935 */
936 public static boolean isVisible(Item i) {
937 if (DisplayController.isTwinFramesOn()) {
938 if (!isVisible(DisplayController.getFrameOnSide(TwinFramesSide.LEFT), i)) {
939 return isVisible(DisplayController.getFrameOnSide(TwinFramesSide.RIGHT), i);
940 } else {
941 return true;
942 }
943 } else {
944 return isVisible(DisplayController.getCurrentFrame(), i);
945 }
946 }
947
948 /**
949 * Determines if an item is visible from a given frame. If the item is free
950 * then it is considered visible.
951 *
952 * @param fromFrame
953 * The frame to check from.
954 * @param i
955 * The item to check
956 * @return True if visible/free from given frame.
957 */
958 public static boolean isVisible(Frame fromFrame, Item i)
959 {
960 if (fromFrame == null) {
961 return false;
962 }
963
964 Frame parent = i.getParent();
965
966 if (parent == fromFrame) {
967 return true;
968 } else if (parent == null) {
969 return FreeItems.getInstance().contains(i) || FreeItems.getCursor().contains(i);
970 }
971
972 return fromFrame.getAllItems().contains(i) && i.isVisible();
973 }
974
975 public static AxisAlignedBoxBounds expandRectangle(AxisAlignedBoxBounds r, int n)
976 {
977 if (r == null) {
978 return null;
979 }
980
981 return new AxisAlignedBoxBounds(r.getMinX() - (n >> 1), r.getMinY() - (n >> 1), r.getWidth() + n, r.getHeight() + n);
982 }
983
984 /*
985 * FrameMouseActions while (!copies.isEmpty()) { Iterator<Item> iterator =
986 * copies.iterator(); Item item = iterator.next(); // Dont paint annotation
987 * items for @v if (!item.isVisible() || item.isAnnotation()) {
988 * iterator.remove(); continue; }
989 *
990 * if (!(item instanceof Line)) { item.setThickness(item.getThickness() *
991 * scale); if (item instanceof XRayable || item.hasEnclosures()) {
992 * scaleItem(scale, defaultForeground, defaultBackground, origin.x,
993 * origin.y, item); items.add(item); copies.remove(item); } else {
994 * Collection<Item> connected = item.getAllConnected(); // Get all the
995 * connected items because we can only set the // thickness ONCE for (Item i :
996 * connected) { scaleItem(scale, defaultForeground, defaultBackground,
997 * origin.x, origin.y, i); } items.addAll(connected);
998 * copies.removeAll(connected); } } else { iterator.remove(); } }
999 */
1000
1001 /**
1002 * @param scale
1003 * @param defaultForeground
1004 * @param defaultBackground
1005 * @param originX
1006 * @param originY
1007 * @param item
1008 */
1009 private static void scaleItem(Vector v, Item item) {
1010 Float scale = v.Scale;
1011 int originX = v.Origin.getX();
1012 int originY = v.Origin.getY();
1013 Colour defaultForeground = v.Foreground;
1014 Colour defaultBackground = v.Background;
1015 UserAppliedPermission permission = v.permission;
1016 // TODO should this be checking if the frame has the
1017 // same permissions as the vector
1018 // and if so don't set the item's permissions?
1019 item.setOverlayPermission(permission);
1020
1021 // TODO encapsulate this somewhere inside of circle class!
1022 // if(item instanceof Circle){
1023 // scaleItem(v, ((Circle)item).getCenter());
1024 // }
1025
1026 if (!(item instanceof Line)) {
1027 if (item.getColor() == null) {
1028 item.setColor(defaultForeground);
1029 }
1030 if (item.getBackgroundColor() == null) {
1031 item.setBackgroundColor(defaultBackground);
1032 }
1033 if (item.getFillColor() == null) {
1034 item.setFillColor(defaultBackground);
1035 }
1036
1037 if (permission.equals(UserAppliedPermission.none)) {
1038 item.setLinkMark(false);
1039 item.setActionMark(false);
1040 }
1041
1042 item.scale(scale, originX, originY);
1043 }
1044 }
1045
1046 /**
1047 * Extracts widgets from an item list.
1048 *
1049 * @param items
1050 * Items to extract from. Must not be null.
1051 *
1052 * @return List of (unique)widgets in items. Never null.
1053 */
1054 public static List<Widget> extractWidgets(List<Item> items) {
1055 assert (items != null);
1056
1057 List<Widget> iWidgets = new LinkedList<Widget>();
1058
1059 for (Item i : items) {
1060 if (i instanceof WidgetEdge) {
1061 WidgetEdge we = (WidgetEdge) i;
1062 if (!iWidgets.contains(we.getWidgetSource())) {
1063 iWidgets.add(we.getWidgetSource());
1064 }
1065 } else if (i instanceof WidgetCorner) {
1066 WidgetCorner wc = (WidgetCorner) i;
1067 if (!iWidgets.contains(wc.getWidgetSource())) {
1068 iWidgets.add(wc.getWidgetSource());
1069 }
1070 }
1071 }
1072
1073 return iWidgets;
1074 }
1075
1076 /**
1077 * Wraps any text items to the size of their container, or the frame size if they have not container
1078 *
1079 * @param items A list of Items to wrap (non-Text items are ignored)
1080 */
1081 public static void Justify(Collection<Item> items) {
1082 for (Item i : items) {
1083 if (i instanceof Text) {
1084 Collection<Item> enclosure = FrameUtils.getEnclosingLineEnds(i.getPosition());
1085 ((Text)i).justify(false, enclosure != null ? enclosure.iterator().next().getEnclosedShape() : null);
1086 }
1087 }
1088 }
1089
1090 /**
1091 * Recalculates containers on the frame, then wraps all text items
1092 *
1093 * @param frame
1094 */
1095 public static void Justify(Frame frame)
1096 {
1097 if (frame == null) {
1098 return;
1099 }
1100 EnclosedCheck(frame.getItems());
1101 Justify(frame.getItems());
1102 }
1103}
Note: See TracBrowser for help on using the repository browser.