[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] | 19 | package org.expeditee.items;
|
---|
| 20 |
|
---|
| 21 | import java.io.File;
|
---|
| 22 | import java.io.UnsupportedEncodingException;
|
---|
| 23 | import java.net.URL;
|
---|
| 24 | import java.net.URLDecoder;
|
---|
| 25 | import java.util.ArrayList;
|
---|
[70] | 26 | import java.util.Collection;
|
---|
| 27 | import java.util.HashMap;
|
---|
[72] | 28 | import java.util.LinkedHashSet;
|
---|
[4] | 29 | import java.util.LinkedList;
|
---|
| 30 | import java.util.List;
|
---|
[70] | 31 | import java.util.Map;
|
---|
[4] | 32 |
|
---|
[1102] | 33 | import org.expeditee.core.Colour;
|
---|
| 34 | import org.expeditee.core.bounds.AxisAlignedBoxBounds;
|
---|
| 35 | import org.expeditee.gui.DisplayController;
|
---|
| 36 | import org.expeditee.gui.DisplayController.TwinFramesSide;
|
---|
[7] | 37 | import org.expeditee.gui.Frame;
|
---|
[920] | 38 | import org.expeditee.gui.FrameUtils;
|
---|
[121] | 39 | import org.expeditee.gui.FreeItems;
|
---|
[115] | 40 | import org.expeditee.gui.Vector;
|
---|
| 41 | import org.expeditee.items.Item.HighlightMode;
|
---|
[198] | 42 | import org.expeditee.items.widgets.InteractiveWidgetInitialisationFailedException;
|
---|
| 43 | import org.expeditee.items.widgets.InteractiveWidgetNotAvailableException;
|
---|
[1190] | 44 | import org.expeditee.items.widgets.Widget;
|
---|
[198] | 45 | import org.expeditee.items.widgets.WidgetCorner;
|
---|
| 46 | import org.expeditee.items.widgets.WidgetEdge;
|
---|
[838] | 47 | import org.expeditee.network.FrameShare;
|
---|
[778] | 48 | import 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 | */
|
---|
| 56 | public 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 | }
|
---|