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

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

Renamed Frame.getItems() to Frame.getSortedItems() to better represent its functionality.

-> org.apollo.ApolloGestureActions
-> org.apollo.ApolloSystem
-> org.expeditee.actions.Actions
-> org.expeditee.actions.Debug
-> org.expeditee.actions.ExploratorySearchActions
-> org.expeditee.actions.JfxBrowserActions
-> org.expeditee.actions.Misc
-> org.expeditee.actions.Navigation
-> org.expeditee.actions.ScriptBase
-> org.expeditee.actions.Simple
-> org.expeditee.agents.ComputeTree
-> org.expeditee.agents.CopyTree
-> org.expeditee.agents.DisplayComet
-> org.expeditee.agents.DisplayTree
-> org.expeditee.agents.DisplayTreeLeaves
-> org.expeditee.agents.GraphFramesetLinks
-> org.expeditee.agents.TreeProcessor
-> org.expeditee.gio.gesture.StandardGestureActions
-> org.expeditee.gui.DisplayController
-> org.expeditee.gui.FrameCreator
-> org.expeditee.gui.FrameIO
-> org.expeditee.io.DefaultTreeWriter
-> org.expeditee.io.JavaWriter
-> org.expeditee.io.PDF2Writer
-> org.expeditee.io.TXTWriter
-> org.expeditee.io.WebParser
-> org.expeditee.io.flowlayout.XGroupItem
-> org.expeditee.items.Dot
-> org.expeditee.items.Item
-> org.expeditee.items.ItemUtils
-> org.expeditee.network.FrameShare
-> org.expeditee.stats.TreeStats


Created ItemsList class to wrap ArrayList<Item>. Frames now use this new class to store its body list (used for display) as well as its primaryBody and surrogateBody.

-> org.expeditee.agents.Format
-> org.expeditee.agents.HFormat
-> org.expeditee.gio.gesture.StandardGestureActions
-> org.expeditee.gui.Frame
-> org.expeditee.gui.FrameUtils


Refactorted Frame.setResort(bool) to Frame.invalidateSorted() to better function how it is intended to with a more accurate name.

-> org.expeditee.agents.Sort


When writing out .exp files and getting attributes to respond to LEFT + RIGHT click, boolean items are by default true. This has always been the case. An ammendment to this is that defaults can now be established.
Also added 'EnterClick' functionality. If cursored over a item with this property and you press enter, it acts as if you have clicked on it instead.

-> org.expeditee.assets.resources-public.framesets.authentication.1.exp to 6.exp
-> org.expeditee.gio.gesture.StandardGestureActions
-> org.expeditee.gio.input.KBMInputEvent
-> org.expeditee.gio.javafx.JavaFXConversions
-> org.expeditee.gio.swing.SwingConversions
-> org.expeditee.gui.AttributeUtils
-> org.expeditee.io.Conversion
-> org.expeditee.io.DefaultFrameWriter
-> org.expeditee.items.Item


Fixed a bug caused by calling Math.abs on Integer.MIN_VALUE returning unexpected result. Due to zero being a thing, you cannot represent Math.abs(Integer.MIN_VALUE) in a Integer object. The solution is to use Integer.MIN_VALUE + 1 instead of Integer.MIN_VALUE.

-> org.expeditee.core.bounds.CombinationBounds
-> org.expeditee.io.flowlayout.DimensionExtent


Recoded the contains function in EllipticalBounds so that intersection tests containing circles work correctly.

-> org.expeditee.core.bounds.EllipticalBounds


Added toString() to PolygonBounds to allow for useful printing during debugging.

-> org.expeditee.core.bounds.PolygonBounds

Implemented Surrogate Mode!

-> org.expeditee.encryption.io.EncryptedExpReader
-> org.expeditee.encryption.io.EncryptedExpWriter
-> org.expeditee.encryption.items.surrogates.EncryptionDetail
-> org.expeditee.encryption.items.surrogates.Label
-> org.expeditee.gui.FrameUtils
-> org.expeditee.gui.ItemsList
-> org.expeditee.items.Item
-> org.expeditee.items.Text


???? Use Integer.MAX_VALUE cast to a float instead of Float.MAX_VALUE. This fixed some bug which I cannot remember.

-> org.expeditee.gio.TextLayoutManager
-> org.expeditee.gio.swing.SwingTextLayoutManager


Improved solution for dealing with the F10 key taking focus away from Expeditee due to it being a assessibility key.

-> org.expeditee.gio.swing.SwingInputManager


Renamed variable visibleItems in FrameGraphics.paintFrame to itemsToPaintCanditates to better represent functional intent.

-> org.expeditee.gui.FrameGraphics


Improved checking for if personal resources exist before recreating them

-> org.expeditee.gui.FrameIO


Repeated messages to message bay now have a visual feedback instead of just a beep. This visual feedback is in the form of a count of the amount of times it has repeated.

-> org.expeditee.gui.MessageBay


Updated comment on the Vector class to explain what vectors are.

-> org.expeditee.gui.Vector


Added constants to represent all of the property keys in DefaultFrameReader and DefaultFrameWriter.

-> org.expeditee.io.DefaultFrameReader
-> org.expeditee.io.DefaultFrameWriter


Updated the KeyList setting to be more heirarcial with how users will store their Secrets.

-> org.expeditee.settings.identity.secrets.KeyList

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 = start.getParent().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().getSortedItems());
751 }
752
753 public static void Old() {
754 OldEnclosedCheck(DisplayController.getCurrentFrame().getSortedItems());
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.getSortedItems());
1101 Justify(frame.getSortedItems());
1102 }
1103}
Note: See TracBrowser for help on using the repository browser.