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
RevLine 
[919]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
[4]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;
[70]26import java.util.Collection;
27import java.util.HashMap;
[72]28import java.util.LinkedHashSet;
[4]29import java.util.LinkedList;
30import java.util.List;
[70]31import java.util.Map;
[4]32
[1102]33import org.expeditee.core.Colour;
34import org.expeditee.core.bounds.AxisAlignedBoxBounds;
35import org.expeditee.gui.DisplayController;
36import org.expeditee.gui.DisplayController.TwinFramesSide;
[7]37import org.expeditee.gui.Frame;
[920]38import org.expeditee.gui.FrameUtils;
[121]39import org.expeditee.gui.FreeItems;
[115]40import org.expeditee.gui.Vector;
41import org.expeditee.items.Item.HighlightMode;
[198]42import org.expeditee.items.widgets.InteractiveWidgetInitialisationFailedException;
43import org.expeditee.items.widgets.InteractiveWidgetNotAvailableException;
[1190]44import org.expeditee.items.widgets.Widget;
[198]45import org.expeditee.items.widgets.WidgetCorner;
46import org.expeditee.items.widgets.WidgetEdge;
[838]47import org.expeditee.network.FrameShare;
[778]48import org.expeditee.settings.folders.FolderSettings;
[4]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
[10]90 // Brook: Im claiming this number!
91 public static final int TAG_IWIDGET = 16;
[4]92
[247]93 public static final int TAG_DOT_TEMPLATE = 17;
[70]94
[72]95 public static final int TAG_STAT_TEMPLATE = 18;
[80]96
[78]97 public static final int TAG_VECTOR = 19;
[427]98
[154]99 public static final int TAG_ACTIVE_VECTOR = 21;
[80]100
[78]101 public static final int TAG_BITMAP_IMAGE = 20;
[72]102
[70]103 public static final int TAG_MIN = 0;
104
[154]105 public static final int TAG_MAX = 21;
[70]106
[4]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 */
[156]118 public static boolean ContainsTag(Collection<Item> items, int tag) {
[4]119 return ContainsTag(items, GetTag(tag));
120 }
121
[156]122 public static boolean ContainsTag(Collection<Item> items, String tag) {
[4]123 return (FindTag(items, tag) != null);
124 }
125
[156]126 public static boolean ContainsExactTag(Collection<Item> items, int tag) {
[70]127 return ContainsExactTag(items, GetTag(tag));
128 }
129
[156]130 public static boolean ContainsExactTag(Collection<Item> items, String tag) {
[70]131 return (FindExactTag(items, tag) != null);
132 }
133
[4]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 */
[78]146 public static Item FindTag(List<Item> items, int tag) {
[4]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 */
[156]160 public static Text FindTag(Collection<Item> items, String toFind) {
[4]161 for (Item i : items) {
[1190]162 if (i instanceof Text && i.isAnnotation()) {
163 if (((Text) i).startsWith(toFind)) {
[4]164 return (Text) i;
[1190]165 }
166 }
[4]167 }
168 return null;
169 }
170
[156]171 public static Item FindExactTag(Collection<Item> items, String toFind) {
[70]172 for (Item i : items) {
[1190]173 if (i instanceof Text && i.isAnnotation()) {
174 if (((Text) i).getText().trim().equalsIgnoreCase(toFind)) {
175 return i;
176 }
177 }
[70]178 }
179
180 return null;
181 }
182
[78]183 public static Item FindExactTag(List<Item> items, int tag) {
[70]184 return FindExactTag(items, GetTag(tag));
185 }
186
[4]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 */
[80]197 public static boolean startsWithTag(Item toCheck, int tag) {
198 return startsWithTag(toCheck, GetTag(tag));
[4]199 }
[72]200
[80]201 public static boolean startsWithTag(Item toCheck, int tag, boolean hasValue) {
202 return startsWithTag(toCheck, GetTag(tag), hasValue);
[70]203 }
[4]204
205 /**
[80]206 * Checks if the given Item begins with the desired tag (case insensitive).
[4]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
[70]212 * @param tagOnly
213 * True if the tag does not have a value
[4]214 * @return True if the tag is found in the given Item, False otherwise.
215 */
[97]216 public static boolean startsWithTag(Item toCheck, String tag,
217 boolean valueAllowed) {
[1190]218 if (!(toCheck instanceof Text)) {
[4]219 return false;
[1190]220 }
[4]221
222 Text txt = (Text) toCheck;
[80]223 String value = ItemUtils.StripTag(txt.getText(), tag);
224
[1190]225 if (value == null) {
[80]226 return false;
[1190]227 }
[80]228 return valueAllowed || value.equals("");
[70]229 }
[4]230
[80]231 /**
232 * Checks if the item begins with the desired tag.
[97]233 *
[80]234 * @param toCheck
235 * @param tag
236 * @return
237 */
238 public static boolean startsWithTag(Item toCheck, String tag) {
239 return startsWithTag(toCheck, tag, true);
[4]240 }
241
242 /**
243 * Strips off the given tag from the given String, and returns wathever is
[86]244 * left. Dont put the colon after tags as it is not needed.
[4]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) {
[1190]254 if (toStrip == null) {
[80]255 return null;
[1190]256 }
[80]257 toStrip = toStrip.trim();
[1190]258 if (!toStrip.toLowerCase().startsWith(tag.toLowerCase())) {
[80]259 return null;
[1190]260 }
[4]261
[1190]262 if (toStrip.length() == tag.length()) {
[80]263 return "";
[1190]264 }
[80]265 // remove tag and ensure the char is the tag separator
266 char separator = toStrip.charAt(tag.length());
[1190]267 if (separator != ':') {
[80]268 return null;
[1190]269 }
[4]270
[1190]271 if (toStrip.length() == tag.length() + 1) {
[80]272 return "";
[1190]273 }
[4]274
[80]275 return toStrip.substring(tag.length() + 1).trim();
[4]276 }
277
278 /**
[80]279 * Strips the first character from a string if it is the tag symbol and
280 * returns the remainder.
[97]281 *
[4]282 * @param toStrip
283 * the string to be stripped
[80]284 * @return the stripped version of the string.
[4]285 */
286 public static String StripTagSymbol(String toStrip) {
287 // there must be something left after stripping
288 if (toStrip != null) {
[80]289 toStrip = toStrip.trim();
[4]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) {
[72]311 // TODO refactor so that this uses a map for INT to tags
[4]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";
[78]321 case TAG_VECTOR:
322 return "@v";
[154]323 case TAG_ACTIVE_VECTOR:
324 return "@av";
[4]325 case TAG_ACTIVE_OVERLAY:
326 return "@ao";
327 case TAG_IMAGE:
[80]328 return "@i";
[4]329 case TAG_ITEM_TEMPLATE:
330 return "@itemtemplate";
331 case TAG_ANNOTATION_TEMPLATE:
332 return "@annotationtemplate";
[72]333 case TAG_STAT_TEMPLATE:
334 return "@stattemplate";
[4]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";
[78]347 case TAG_BITMAP_IMAGE:
348 return "@b";
[4]349 case TAG_BACKUP:
350 return "@old";
351 case TAG_POINTTYPE:
[80]352 return "@pointtype";
[10]353 case TAG_IWIDGET:
354 return "@iw";
[247]355 case TAG_DOT_TEMPLATE:
356 return "@dottemplate";
[4]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 *
[601]372 * @param source
[1102]373 * The Text file containing the Picture information
[4]374 * @return The Picture object representing the file, or Null if the file is
375 * not found.
376 */
[1102]377 public static Picture CreatePicture(Text source, boolean tryRemote) {
[80]378 String text = source.getText();
[4]379 String path = "";
[601]380 String fileName = "";
[4]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
[80]389 int fileSuffixChar = text.indexOf('.');
[1190]390 if (fileSuffixChar < 0) {
[80]391 return null;
[1190]392 }
[80]393 int endOfFileName = text.indexOf(' ', fileSuffixChar);
394 if (endOfFileName < 0) {
[4]395 path = text;
[80]396 size = "";
397 } else {
398 path = text.substring(0, endOfFileName);
399 size = text.substring(endOfFileName).trim();
400 }
[601]401 fileName = path;
[4]402
[601]403 // try images subdirectory
404 File file = null;
405
[1245]406 for (String dir : FolderSettings.ImageDirs.getAbsoluteDirs()) {
[601]407 file = new File(dir + path);
[1190]408 if (file.exists() && !file.isDirectory()) {
[601]409 break;
[1190]410 }
[601]411 }
412
[1190]413 if (file == null || !file.exists() || file.isDirectory()) {
[601]414 file = new File(path);
[1190]415 }
[601]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
[1190]431 } else {
[601]432 path = file.getPath();
[1190]433 }
[601]434
[838]435 // if the image isn't found by now, try remote servers
[601]436 file = new File(path);
437 if (!file.exists() || file.isDirectory()) {
[838]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
[1102]441 return CreatePicture(source, false);
[838]442 }
[601]443 return null;
444 }
445
[4]446 } catch (Exception e) {
447 return null;
448 }
449
450 try {
[1102]451 Picture pic = new Picture(source, fileName, path, size);
[601]452
[592]453 return pic;
[4]454 } catch (Exception e) {
455 e.printStackTrace();
456 return null;
457 }
458
459 }
[838]460
[1102]461 public static Picture CreatePicture(Text source) {
462 return CreatePicture(source, true);
[838]463 }
[4]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 */
[70]472 public static List<Item> CopyItems(Collection<Item> toCopy) {
[115]473 return CopyItems(toCopy, false, null);
[7]474 }
[70]475
[115]476 public static List<Item> CopyItems(Collection<Item> toCopy, Vector v) {
477 return CopyItems(toCopy, false, v);
478 }
479
[70]480 public static List<Item> CopyItems(Collection<Item> toCopy, boolean extrude) {
[115]481 return CopyItems(toCopy, extrude, null);
482 }
483
484 public static List<Item> CopyItems(Collection<Item> toCopy,
485 boolean extrude, Vector v) {
[4]486 // The copies to return
487 List<Item> copies = new ArrayList<Item>();
488
489 // list of dots at the end of lines
[78]490 Collection<Item> lineEnds = new LinkedHashSet<Item>();
491 Collection<Line> lines = new LinkedHashSet<Line>();
[108]492 Collection<XRayable> xrayables = new LinkedHashSet<XRayable>();
[78]493 Collection<Constraint> constraints = new LinkedHashSet<Constraint>();
[4]494
[78]495 Collection<Item> singles = new LinkedHashSet<Item>();
[4]496
[70]497 Map<Item, Item> lineEndMap = new HashMap<Item, Item>();
498
[10]499 // Widgets are super special
[1102]500 List<Widget> widgets = new ArrayList<Widget>();
[70]501
[4]502 for (Item i : toCopy) {
[427]503 // Dont copy parts of a vector
[1190]504 if (i == null || !i.hasPermission(UserAppliedPermission.copy)) {
[286]505 continue;
[1190]506 }
[427]507
[70]508 // BROOK
509 if (i instanceof WidgetCorner) { // dont add these
510 if (!widgets.contains(((WidgetCorner) i).getWidgetSource()))
[1190]511 {
[70]512 widgets.add(((WidgetCorner) i).getWidgetSource());
513 // BROOK
[1190]514 }
[70]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);
[108]519 } else if (i instanceof XRayable) {
[115]520 xrayables.add((XRayable) i);
[70]521 } else {
522 if (i.isLineEnd()) {
523 lineEnds.add(i);
524 constraints.addAll(i.getConstraints());
525 } else {
526 singles.add(i);
[4]527 }
528 }
529 }
[115]530
531 // Dont copy the other items that are part of the circle
532 for (XRayable x : xrayables) {
[108]533 Collection<Item> connected = x.getConnected();
534 singles.removeAll(connected);
535 lineEnds.removeAll(connected);
536 lines.removeAll(connected);
[115]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 }
[108]543 }
[115]544
[70]545 // copy all single items
546 for (Item i : singles) {
[4]547 Item copy = i.copy();
[115]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);
[4]553 // if this is the frame name, make sure the frame is saved (in
554 // case it is a TDFC frame)
[115]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 }
[4]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) {
[70]570 // create a copy of the line end
571 Item copy = i.copy();
572 copy.removeAllLines();
573 copy.removeAllConstraints();
[4]574
[70]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);
[4]582 }
[70]583 copies.add(copy);
584 lineEndMap.put(i, copy);
[115]585 // Scale items that are from a vector frame
586 if (v != null) {
587 scaleItem(v, copy);
588 }
[4]589 }
590
591 // recreate lines
[70]592 for (Line line : lines) {
593 Line lineCopy = line.copy();
[80]594 // get the lineEnd we copied above if it is in the MAPPING
[78]595 Item originalLineEnd = line.getEndItem();
596 Item actualLineEnd = lineEndMap.get(originalLineEnd);
[1190]597 if (actualLineEnd == null) {
[78]598 lineCopy.setEndItem(originalLineEnd);
[1190]599 } else {
[78]600 lineCopy.setEndItem(actualLineEnd);
[1190]601 }
[80]602
[78]603 Item originalLineStart = line.getStartItem();
604 Item actualLineStart = lineEndMap.get(originalLineStart);
[1190]605 if (actualLineStart == null) {
[78]606 lineCopy.setStartItem(originalLineStart);
[1190]607 } else {
[78]608 lineCopy.setStartItem(actualLineStart);
[1190]609 }
[80]610
[70]611 copies.add(lineCopy);
[4]612 }
613
[70]614 // recreate constraints
615 for (Constraint c : constraints) {
616 Item start = lineEndMap.get(c.getStart());
617 Item end = lineEndMap.get(c.getEnd());
[1102]618 int id = DisplayController.getCurrentFrame().getNextItemID();
[70]619 if (start != null && end != null) {
620 new Constraint(start, end, id, c.getType());
621 }
[4]622 }
623
[10]624 // BROOK
[1102]625 for (Widget iw : widgets) {
[10]626 try {
[70]627
[1102]628 Widget icopy = iw.copy();
[10]629 copies.addAll(icopy.getItems());
[70]630
[10]631 } catch (InteractiveWidgetNotAvailableException e) {
632 e.printStackTrace();
[81]633 } catch (InteractiveWidgetInitialisationFailedException e) {
634 e.printStackTrace();
[10]635 }
[97]636
[10]637 }
[427]638
639 // Make sure filled rectangles are shown filled on vector overlays
[1190]640 if (v != null) {
[115]641 EnclosedCheck(copies);
[1190]642 }
[70]643
[4]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 */
[97]658 public static List<Item> UnreelLine(Item toUnreelFrom, boolean constrain) {
[4]659 // the Item must already have one line to be unreeled from
[1190]660 if (toUnreelFrom == null || toUnreelFrom.getLines().size() < 1) {
[4]661 return null;
[1190]662 }
[4]663
664 List<Item> unreel = new ArrayList<Item>(2);
665 unreel.add(toUnreelFrom);
666 unreel.addAll(toUnreelFrom.getLines());
[97]667 return UnreelLine(unreel, constrain);
[4]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 */
[97]682 public static List<Item> UnreelLine(List<Item> toUnreel, boolean constrain) {
[4]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
[115]687 if (item.hasEnclosures()
688 || !(item.isLineEnd() || item instanceof Line)) {
[4]689 return null;
690 }
[70]691 // find the dot to unreel from
692 if (item.isLineEnd()) {
[4]693 // if there are multiple ends in the list, return
[1190]694 if (origEnd != null) {
[4]695 return null;
[1190]696 }
[4]697
698 origEnd = item;
699 }
700 }
701
702 // copy the original endpoint
703 Item copy = origEnd.copy();
[115]704 origEnd.setHighlightMode(HighlightMode.None);
[1102]705 origEnd.setHighlightColorToDefault();
[4]706 copy.removeAllLines();
707 copy.removeAllConstraints();
708
[427]709 for (Line l : origEnd.getLines()) {
[407]710 l.invalidateAll();
711 }
[427]712
[4]713 // create a new line
[1102]714 Frame currentFrame = DisplayController.getCurrentFrame();
[97]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
[108]718 // TODO add later a diagonal constraint if getLines() == 3 or 4
[97]719 Collection<Constraint> constraints = origEnd.getConstraints();
[1102]720 if (constrain && constraints.size() > 0 && origEnd.getLines().size() == 2) {
[97]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;
[108]727 }
728 if (c.getType() == Constraint.DIAGONAL_NEG) {
[97]729 type = Constraint.DIAGONAL_POS;
730 } else if (c.getType() == Constraint.DIAGONAL_POS) {
731 type = Constraint.DIAGONAL_NEG;
732 }
733 }
734 if (type != null) {
[1102]735 new Constraint(origEnd, copy, currentFrame.getNextItemID(), type);
[97]736 }
737 }
738
[70]739 // copy.setFloating(true);
[4]740 origEnd.setArrowheadLength(0);
[70]741 // copy.setArrowheadLength(0);
[4]742
743 List<Item> toReturn = new LinkedList<Item>();
744 toReturn.add(copy);
745 toReturn.add(line);
746 return toReturn;
747 }
748
[72]749 public static void New() {
[1102]750 EnclosedCheck(DisplayController.getCurrentFrame().getItems());
[72]751 }
752
753 public static void Old() {
[1102]754 OldEnclosedCheck(DisplayController.getCurrentFrame().getItems());
[72]755 }
756
[4]757 /**
[72]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
[1190]772 if (lineEnds.size() == 0) {
[72]773 return;
[1190]774 }
[72]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();
[1190]783 for (Item i : connected) {
[72]784 i.setConnectedToAnnotation(true);
[1190]785 }
[72]786 lineEnds.removeAll(connected);
787 }
788 lineEnds.remove(item);
789 }
790 }
791
792 /**
[4]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 */
[72]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
[1190]807 if (i.getLines().size() == 2) {
[72]808 lineEnds.add(i);
[1190]809 }
[72]810 }
811 }
812
813 // if there are no line endpoints on the Frame, then there can't be an
814 // enclosure
[1190]815 if (lineEnds.size() == 0) {
[72]816 return;
[1190]817 }
[72]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) {
[1190]826 if (itemToCheck.isLineEnd()) {
[72]827 connectedLineEnds.add(itemToCheck);
[1190]828 }
[72]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) {
[4]855 _seen.clear();
856
[50]857 // get all lineEnds on the Frame
858 List<Item> lineEnds = new ArrayList<Item>(0);
[4]859 for (Item i : items) {
[50]860 if (i.isLineEnd()) {
861 i.setEnclosedList(null);
[4]862
[1190]863 if (i.getLines().size() == 2) {
[50]864 lineEnds.add(i);
[1190]865 }
[4]866 }
867 }
868
[70]869 // if there are no line endpoints on the Frame, then there can't be an
870 // enclosure
[1190]871 if (lineEnds.size() == 0) {
[4]872 return;
[1190]873 }
[4]874
[70]875 // TODO optimise this code!!
[50]876 // iterate through all the lineEnds
877 for (Item searchFor : lineEnds) {
[4]878 _seen.clear();
879
880 for (Line l : searchFor.getLines()) {
881 _seen.add(l);
882 if (traverse(searchFor, l.getOppositeEnd(searchFor))) {
[50]883 _path.add(l.getOppositeEnd(searchFor));
[4]884
[1190]885 for (Item i : _path) {
[50]886 i.setEnclosedList(_path);
[1190]887 }
[4]888
[50]889 _path = new ArrayList<Item>(0);
[4]890
891 break;
892 }
893 }
894 }
895 }
896
897 private static List<Line> _seen = new ArrayList<Line>();
898
[50]899 private static List<Item> _path = new ArrayList<Item>();
[4]900
[50]901 private static boolean traverse(Item toFind, Item searchFrom) {
[1190]902 if (toFind == null || searchFrom == null || !searchFrom.isLineEnd()) {
[4]903 return false;
[1190]904 }
[4]905
[1190]906 if (searchFrom.getLines().size() != 2) {
[4]907 return false;
[1190]908 }
[4]909
[1190]910 if (toFind == searchFrom) {
[4]911 return true;
[1190]912 }
[4]913
914 for (Line l : searchFrom.getLines()) {
915 if (!(_seen.contains(l))) {
916 _seen.add(l);
917 if (traverse(toFind, l.getOppositeEnd(searchFrom))) {
[50]918 _path.add(l.getOppositeEnd(searchFrom));
[4]919 return true;
920 }
921 }
922
923 }
924
925 return false;
926 }
[115]927
[121]928 /**
[427]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
[121]934 * @return True if visible/free from given frame.
935 */
936 public static boolean isVisible(Item i) {
[1102]937 if (DisplayController.isTwinFramesOn()) {
938 if (!isVisible(DisplayController.getFrameOnSide(TwinFramesSide.LEFT), i)) {
939 return isVisible(DisplayController.getFrameOnSide(TwinFramesSide.RIGHT), i);
[121]940 } else {
941 return true;
942 }
[427]943 } else {
[1102]944 return isVisible(DisplayController.getCurrentFrame(), i);
[121]945 }
946 }
947
948 /**
[427]949 * Determines if an item is visible from a given frame. If the item is free
950 * then it is considered visible.
[121]951 *
[427]952 * @param fromFrame
953 * The frame to check from.
954 * @param i
955 * The item to check
[121]956 * @return True if visible/free from given frame.
957 */
[1102]958 public static boolean isVisible(Frame fromFrame, Item i)
959 {
[1190]960 if (fromFrame == null) {
961 return false;
962 }
[427]963
[121]964 Frame parent = i.getParent();
[427]965
[1102]966 if (parent == fromFrame) {
[427]967 return true;
[1102]968 } else if (parent == null) {
969 return FreeItems.getInstance().contains(i) || FreeItems.getCursor().contains(i);
970 }
[121]971
972 return fromFrame.getAllItems().contains(i) && i.isVisible();
973 }
[427]974
[1102]975 public static AxisAlignedBoxBounds expandRectangle(AxisAlignedBoxBounds r, int n)
976 {
[1190]977 if (r == null) {
978 return null;
979 }
[1102]980
981 return new AxisAlignedBoxBounds(r.getMinX() - (n >> 1), r.getMinY() - (n >> 1), r.getWidth() + n, r.getHeight() + n);
[121]982 }
[427]983
[115]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;
[1143]1011 int originX = v.Origin.getX();
1012 int originY = v.Origin.getY();
[1102]1013 Colour defaultForeground = v.Foreground;
1014 Colour defaultBackground = v.Background;
[450]1015 UserAppliedPermission permission = v.permission;
[737]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);
[115]1020
[427]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)) {
[115]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 }
[311]1036
[450]1037 if (permission.equals(UserAppliedPermission.none)) {
[115]1038 item.setLinkMark(false);
1039 item.setActionMark(false);
1040 }
[427]1041
[311]1042 item.scale(scale, originX, originY);
[115]1043 }
1044 }
[427]1045
[365]1046 /**
1047 * Extracts widgets from an item list.
1048 *
1049 * @param items
[427]1050 * Items to extract from. Must not be null.
[365]1051 *
[427]1052 * @return List of (unique)widgets in items. Never null.
[365]1053 */
[1102]1054 public static List<Widget> extractWidgets(List<Item> items) {
[427]1055 assert (items != null);
1056
[1102]1057 List<Widget> iWidgets = new LinkedList<Widget>();
[427]1058
[365]1059 for (Item i : items) {
1060 if (i instanceof WidgetEdge) {
[427]1061 WidgetEdge we = (WidgetEdge) i;
[1190]1062 if (!iWidgets.contains(we.getWidgetSource())) {
[365]1063 iWidgets.add(we.getWidgetSource());
[1190]1064 }
[365]1065 } else if (i instanceof WidgetCorner) {
[427]1066 WidgetCorner wc = (WidgetCorner) i;
[1190]1067 if (!iWidgets.contains(wc.getWidgetSource())) {
[365]1068 iWidgets.add(wc.getWidgetSource());
[1190]1069 }
[365]1070 }
1071 }
[427]1072
[365]1073 return iWidgets;
1074 }
[920]1075
[922]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 */
[920]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());
[922]1085 ((Text)i).justify(false, enclosure != null ? enclosure.iterator().next().getEnclosedShape() : null);
[920]1086 }
1087 }
1088 }
1089
[922]1090 /**
1091 * Recalculates containers on the frame, then wraps all text items
1092 *
1093 * @param frame
1094 */
[1102]1095 public static void Justify(Frame frame)
1096 {
[1190]1097 if (frame == null) {
1098 return;
1099 }
[920]1100 EnclosedCheck(frame.getItems());
1101 Justify(frame.getItems());
1102 }
[4]1103}
Note: See TracBrowser for help on using the repository browser.