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

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

Implementation of ProfileManager. Refactor + additional content for how new profiles are created. The refactoring split out the creation of the default profile from user profiles. Refactoring revealed a long term bug that was causing user profiles to generate with incorrect information. The additional content fixed this bug by introducing the ${USER.NAME} variable, so that the default profile frameset can specify resource locations located in the users resource directory.

org.expeditee.auth.AuthenticatorBrowser
org.expeditee.auth.account.Create
org.expeditee.gui.Browser
org.expeditee.gui.management.ProfileManager
org.expeditee.setting.DirectoryListSetting
org.expeditee.setting.ListSetting
org.expeditee.settings.UserSettings

Implementation of ResourceManager as a core location to get resources from the file system. Also the additional variable ${CURRENT_FRAMESET} to represent the current frameset, so that images can be stored in the directory of the current frameset. This increases portability of framesets.

org.expeditee.gui.FrameIO
org.expeditee.gui.management.ResourceManager
org.expeditee.gui.management.ResourceUtil
Audio:

#NB: Audio used to only operate on a single directory. This has been updated to work in a same way as images. That is: when you ask for a specific resouce, it looks to the user settings to find a sequence of directories to look at in order until it manages to find the desired resource.


There is still need however for a single(ish) source of truth for the .banks and .mastermix file. Therefore these files are now always located in resource-<username>\audio.
org.apollo.agents.MelodySearch
org.apollo.audio.structure.AudioStructureModel
org.apollo.audio.util.MultiTrackPlaybackController
org.apollo.audio.util.SoundDesk
org.apollo.gui.FrameLayoutDaemon
org.apollo.io.AudioPathManager
org.apollo.util.AudioPurger
org.apollo.widgets.FramePlayer
org.apollo.widgets.SampledTrack

Images:

org.expeditee.items.ItemUtils

Frames:

org.expeditee.gui.FrameIO

Fixed a error in the FramePlayer class caused by an incorrect use of toArray().

org.apollo.widgets.FramePlayer


Added several short cut keys to allow for the Play/Pause (Ctrl + P), mute (Ctrl + M) and volume up/down (Ctrl + +/-) when hovering over SampledTrack widgets.

org.apollo.widgets.SampledTrack


Changed the way that Authenticate.login parses the new users profile to be more consistance with other similar places in code.

org.expeditee.auth.account.Authenticate


Encapsulated _body, _surrogateItemsBody and _primaryItemsBody in Frame class. Also changed getBody function to take a boolean flag as to if it should respect the current surrogate mode. If it should then it makes sure that labels have not changed since last time getBody was called.

org.expeditee.gui.Frame

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