source: trunk/src/org/expeditee/gui/FrameUtils.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: 66.3 KB
Line 
1/**
2 * FrameUtils.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.gui;
20
21import java.io.File;
22import java.io.FileInputStream;
23import java.io.FileOutputStream;
24import java.io.IOException;
25import java.io.InputStream;
26import java.lang.reflect.InvocationTargetException;
27import java.net.JarURLConnection;
28import java.net.URISyntaxException;
29import java.net.URL;
30import java.net.URLConnection;
31import java.nio.file.FileVisitResult;
32import java.nio.file.FileVisitor;
33import java.nio.file.Files;
34import java.nio.file.Path;
35import java.nio.file.Paths;
36import java.nio.file.attribute.BasicFileAttributes;
37import java.util.ArrayList;
38import java.util.Arrays;
39import java.util.Collection;
40import java.util.Collections;
41import java.util.Comparator;
42import java.util.Enumeration;
43import java.util.LinkedHashSet;
44import java.util.LinkedList;
45import java.util.List;
46import java.util.Map;
47import java.util.Scanner;
48import java.util.function.Consumer;
49import java.util.jar.JarEntry;
50import java.util.jar.JarFile;
51import java.util.stream.Collectors;
52import java.util.zip.ZipEntry;
53
54import org.expeditee.agents.ExistingFramesetException;
55import org.expeditee.agents.InvalidFramesetNameException;
56import org.expeditee.auth.AuthenticatorBrowser;
57import org.expeditee.auth.mail.gui.MailBay;
58import org.expeditee.core.Colour;
59import org.expeditee.core.Point;
60import org.expeditee.core.bounds.AxisAlignedBoxBounds;
61import org.expeditee.core.bounds.PolygonBounds;
62import org.expeditee.encryption.items.surrogates.Label;
63import org.expeditee.gio.EcosystemManager;
64import org.expeditee.gio.gesture.StandardGestureActions;
65import org.expeditee.items.Circle;
66import org.expeditee.items.Dot;
67import org.expeditee.items.FrameBitmap;
68import org.expeditee.items.FrameImage;
69import org.expeditee.items.Item;
70import org.expeditee.items.Item.HighlightMode;
71import org.expeditee.items.ItemUtils;
72import org.expeditee.items.JSItem;
73import org.expeditee.items.Line;
74import org.expeditee.items.PermissionTriple;
75import org.expeditee.items.Picture;
76import org.expeditee.items.Text;
77import org.expeditee.items.UserAppliedPermission;
78import org.expeditee.items.XRayable;
79import org.expeditee.items.widgets.ButtonWidget;
80import org.expeditee.items.widgets.InteractiveWidgetInitialisationFailedException;
81import org.expeditee.items.widgets.InteractiveWidgetNotAvailableException;
82import org.expeditee.items.widgets.Widget;
83import org.expeditee.items.widgets.WidgetCorner;
84import org.expeditee.items.widgets.WidgetEdge;
85import org.expeditee.setting.Setting;
86import org.expeditee.settings.Settings;
87import org.expeditee.settings.UserSettings;
88import org.expeditee.settings.templates.TemplateSettings;
89import org.expeditee.stats.Logger;
90import org.expeditee.stats.SessionStats;
91
92public class FrameUtils {
93
94 /**
95 * The list of known start pages framesets which will have prepopulated links in
96 * the home frame.
97 */
98 public static final String[] startPages = { "exploratorysearch", "webbrowser" };
99
100 private static final int COLUMN_WIDTH = 50;
101
102 /**
103 * Provides a way to monitor the time elapsed between button-down and the
104 * finished painting.
105 */
106 public static TimeKeeper ResponseTimer = new TimeKeeper();
107
108 private static float _ResponseTimeSum = 0;
109
110 private static float _LastResponse = 0;
111
112 private static Text LastEdited = null;
113
114 public static int MINIMUM_INTERITEM_SPACING = -6;
115
116 private static Item _tdfcItem = null;
117
118 public static float getResponseTimeTotal() {
119 return _ResponseTimeSum;
120 }
121
122 public static float getLastResponseTime() {
123 return _LastResponse;
124 }
125
126 /**
127 * Checks if the given top Item is above the given bottom Item, allowing for the
128 * X coordinates to be off by a certain width...
129 *
130 * @param item1
131 * The Item to check is above the other Item
132 * @param item2
133 * The Item to check is below the top Item
134 * @return True if top is above bottom, False otherwise.
135 */
136 public static boolean inSameColumn(Item item1, Item item2) {
137 if (!(item1 instanceof Text) || !(item2 instanceof Text)) {
138 return false;
139 }
140
141 if (item1.getID() < 0 || item2.getID() < 0) {
142 return false;
143 }
144
145 int minX = item2.getX();
146 int maxX = item2.getX() + item2.getBoundsWidth();
147
148 int startX = item1.getX();
149 int endX = item1.getX() + item1.getBoundsWidth();
150
151 // Check that the two items left values are close
152 if (Math.abs(item1.getX() - item2.getX()) > COLUMN_WIDTH) {
153 return false;
154 }
155
156 // Ensure the two items
157 if ((minX >= startX && minX <= endX) || (maxX >= startX && maxX <= endX) || (startX >= minX && startX <= maxX)
158 || (endX >= minX && endX <= maxX)) {
159 return true;
160 }
161
162 return false;
163 }
164
165 public static boolean sameBulletType(String bullet1, String bullet2) {
166 if (bullet1 == null || bullet2 == null) {
167 return false;
168 }
169
170 if (bullet1.equals("") || bullet2.equals("")) {
171 return false;
172 }
173
174 if (Character.isLetter(bullet1.charAt(0)) && Character.isLetter(bullet2.charAt(0))) {
175 return true;
176 }
177
178 if (Character.isDigit(bullet1.charAt(0)) && Character.isDigit(bullet2.charAt(0))) {
179 return true;
180 }
181
182 // TODO make this more sofisticated
183
184 return false;
185 }
186
187 private static boolean needsRenumbering(String s) {
188 if (s == null || s.equals("")) {
189 return false;
190 }
191 if (!Character.isLetterOrDigit(s.charAt(0))) {
192 return false;
193 }
194
195 s = s.trim();
196 // if its all letters then we dont want to auto adjust
197 if (s.length() > 2) {
198 for (int i = 0; i < s.length() - 1; i++) {
199 if (!Character.isLetter(s.charAt(i))) {
200 return true;
201 }
202 }
203 } else {
204 return true;
205 }
206
207 return false;
208 }
209
210 /**
211 *
212 * @param toAlign
213 * @param moveAll
214 * @param adjust
215 * @return
216 */
217 public static int Align(List<Text> toAlign, boolean moveAll, int adjust, List<Item> changedItems) {
218 Collections.sort(toAlign);
219
220 /*
221 * Single items dont need alignment But if there are two items we may still want
222 * to format them... ie if they are too close together.
223 */
224 if (toAlign.size() < 1) {
225 return 0;
226 }
227
228 // get the first item
229 Text from = toAlign.get(0);
230 if (from.getParent() == null) {
231 from = toAlign.get(1);
232 }
233 int x = from.getX();
234
235 Frame curr = from.getParent();
236 Text above = curr.getTextAbove(from);
237
238 String lastBullet = "";
239
240 if (above != null && curr.isNormalTextItem(above)) {
241 lastBullet = StandardGestureActions.getAutoBullet(above.getText());
242 } else {
243 lastBullet = StandardGestureActions.getBullet(toAlign.get(0).getText());
244 }
245 if (needsRenumbering(lastBullet)) {
246 // renumber...
247 for (int i = 0; i < toAlign.size(); i++) {
248
249 Text currentText = toAlign.get(i);
250 String currentBullet = StandardGestureActions.getAutoBullet(currentText.getText());
251
252 if (sameBulletType(lastBullet, currentBullet)) {
253 String oldText = currentText.getText();
254
255 currentText.stripFirstWord();
256
257 currentText.setText(lastBullet + currentText.getText());
258 lastBullet = StandardGestureActions.getAutoBullet(currentText.getText());
259
260 // if we changed the item, add to changedItems list
261 if (changedItems != null && oldText != currentText.getText()
262 && !changedItems.contains(currentText)) {
263 Item copy = currentText.copy();
264 copy.setID(currentText.getID());
265 copy.setText(oldText);
266 changedItems.add(copy);
267 }
268 }
269 }
270 }
271
272 // work out the spacing between the first item and the one above it
273
274 int space = 10 + adjust;
275
276 // if we are dropping from the title make the space a little bigger
277 // than normal
278
279 // If there are only two items get the gap from the start item on the
280 // zero frame if there is one
281 if (above == curr.getTitleItem()) {
282 Frame zero = FrameIO.LoadFrame(curr.getFramesetName() + '0');
283 String strGap = zero.getAnnotationValue("start");
284 if (strGap != null) {
285 try {
286 int gap = Integer.parseInt(strGap);
287 space = gap;
288 } catch (NumberFormatException nfe) {
289
290 }
291 }
292 } else if (above != null) {
293 // Make the gap between all items the same as the gap between
294 // the first two
295 space = from.getBounds().getMinY() - above.getBounds().getMaxY();
296
297 if (space < MINIMUM_INTERITEM_SPACING) {
298 space = MINIMUM_INTERITEM_SPACING;
299 }
300
301 if (UserSettings.FormatSpacingMax.get() != null) {
302 double maxSpace = UserSettings.FormatSpacingMax.get() * above.getSize();
303 if (maxSpace < space) {
304 space = (int) Math.round(maxSpace);
305 }
306 }
307
308 if (UserSettings.FormatSpacingMin.get() != null) {
309 double minSpace = UserSettings.FormatSpacingMin.get() * above.getSize();
310 if (minSpace > space) {
311 space = (int) Math.round(minSpace);
312 }
313 }
314
315 // Need to do things differently for FORMAT than for DROPPING
316 if (moveAll && above != curr.getNameItem() && above != curr.getTitleItem()) {
317 x = above.getX();
318 int y = above.getBounds().getMaxY() + space + (from.getY() - from.getBounds().getMinY());
319
320 if (changedItems != null && (from.getX() != x || from.getY() != y) && !changedItems.contains(from)) {
321 Item copy = from.copy();
322 copy.setID(from.getID());
323 changedItems.add(copy);
324 }
325 from.setPosition(x, y);
326 } else {
327 x = from.getX();
328 }
329
330 space += adjust;
331 }
332 for (int i = 1; i < toAlign.size(); i++) {
333 Item current = toAlign.get(i);
334 Item top = toAlign.get(i - 1);
335
336 // The bottom of the previous item
337 int bottom = top.getBounds().getMaxY();
338
339 // the difference between the current item's Y coordinate and
340 // the top of the highlight box
341 int diff = current.getY() - current.getBounds().getMinY();
342
343 int newPos = bottom + space + diff;
344
345 if (changedItems != null && ((moveAll && current.getX() != x) || current.getY() != newPos)
346 && !changedItems.contains(current)) {
347 Item copy = current.copy();
348 copy.setID(current.getID());
349 changedItems.add(copy);
350 }
351
352 if (moveAll) {
353 current.setPosition(x, newPos);
354 } else if (newPos > current.getY()) {
355 current.setY(newPos);
356 }
357
358 }
359
360 // if (insert != null)
361 // return insert.getY();
362
363 // Michael thinks we return the y value for the next new item??
364 int y = from.getY() + from.getBoundsHeight() + space;
365 return y;
366 }
367
368 public static int Align(List<Text> toAlign, boolean moveAll, int adjust) {
369 return Align(toAlign, moveAll, adjust, null);
370 }
371
372 public static boolean LeavingFrame(Frame current) {
373 checkTDFCItemWaiting(current);
374 // active overlay frames may also require saving if they have been
375 // changed
376 for (Overlay o : current.getOverlays()) {
377 if (!SaveCheck(o.Frame)) {
378 return false;
379 }
380 }
381
382 // if the check fails there is no point continuing
383 if (!SaveCheck(current)) {
384 return false;
385 }
386
387 for (Item i : current.getSortedItems()) {
388 i.setHighlightMode(Item.HighlightMode.None);
389 i.setHighlightColorToDefault();
390 }
391 return true;
392 }
393
394 private static boolean SaveCheck(Frame toSave) {
395 // don't bother saving frames that haven't changed
396 if (!toSave.hasChanged()) {
397 return true;
398 }
399
400 // if the frame has been changed, then save it
401 if (DisplayController.isTwinFramesOn()) {
402 Frame opposite = DisplayController.getOppositeFrame();
403
404 String side = "left";
405 if (DisplayController.getCurrentSide() == DisplayController.TwinFramesSide.RIGHT) {
406 side = "right";
407 }
408
409 // if the two frames both have changes, prompt the user for the
410 // next move
411 if (opposite.hasChanged() && opposite.equals(toSave)) {
412 if (EcosystemManager.getGraphicsManager().showDialog("Changes",
413 "Leaving this frame will discard changes made in the " + side + " Frame. Continue?")) {
414 FrameIO.SaveFrame(toSave);
415 DisplayController.Reload(DisplayController.getSideFrameIsOn(opposite));
416 return true;
417 } else {
418 return false;
419 }
420 } else if (opposite.hasOverlay(toSave)) {
421 if (toSave.hasChanged()) {
422 if (EcosystemManager.getGraphicsManager().showDialog("Changes",
423 "Leaving this frame will discard changes made in the " + side + " Frame. Continue?")) {
424 FrameIO.SaveFrame(toSave);
425 DisplayController.Reload(DisplayController.getSideFrameIsOn(opposite));
426 return true;
427 } else {
428 return false;
429 }
430 }
431 }
432
433 // save the current frame and restore the other side
434 FrameIO.SaveFrame(toSave);
435 return true;
436 }
437
438 // single-frame mode can just save and return
439 FrameIO.SaveFrame(toSave);
440 return true;
441 }
442
443 // TODO: consider reloating this method to Frame class?
444 protected static Item getAnnotation(Frame frame, String annotationStr) {
445 Item matched_item = null;
446
447 // check for an updated template...
448 for (Item i : frame.getAnnotationItems()) {
449
450 if (ItemUtils.startsWithTag(i, annotationStr)) {
451
452 matched_item = i;
453 break;
454 }
455 }
456
457 return matched_item;
458 }
459
460 protected static void doFrameTransition(Item frameTransition, Frame from, Frame to) {
461 String s = frameTransition.getText();
462 String[] s_array = s.split(":");
463 if (s_array.length > 1) {
464 String slide_mode_method = s_array[1].trim();
465
466 FrameTransition transition = new FrameTransition(from.getBuffer(), slide_mode_method);
467
468 DisplayController.setTransition(from, transition);
469
470 System.out.println("Triggered on annotation: " + s);
471 } else {
472 System.err.println("Warning: failed to detect frameTransition type");
473 // TODO: print list as a result of reflection listing
474 }
475 }
476
477 /**
478 * Displays the given Frame on the display. If the current frame has changed
479 * since the last save then it will be saved before the switch is made. The
480 * caller can also dictate whether the current frame is added to the back-stack
481 * or not.
482 *
483 * @param toDisplay
484 * The Frame to display on the screen
485 * @param addToBack
486 * True if the current Frame should be added to the back-stack, False
487 * otherwise
488 */
489 public static void DisplayFrame(Frame toDisplay, boolean addToBack, boolean incrementStats) {
490 if (toDisplay == null) {
491 return;
492 }
493
494 // Check if group specified that it exists.
495 String group = toDisplay.getGroup();
496 Frame groupFrame = null;
497 if (group != null && group.length() > 0) {
498 groupFrame = toDisplay.getGroupFrame();
499 if (groupFrame == null) {
500 MessageBay.displayMessage("WARNING: There is no corrosponding " + group + "1 for the specified group " + group + ". Group permissions will not apply.");
501 }
502 }
503
504 final PermissionTriple framePermissions = toDisplay.getPermission();
505 if (framePermissions != null
506 && framePermissions.getPermission(toDisplay.getOwner(), toDisplay.getGroupMembers()) == UserAppliedPermission.denied) {
507 MessageBay.errorMessage("Insufficient permissions to navigate to frame: " + toDisplay.getName());
508 return;
509 }
510
511 Frame current = DisplayController.getCurrentFrame();
512
513 // Dont need to do anything if the frame to display is already being
514 // displayed
515 if (current.equals(toDisplay)) {
516 return;
517 }
518
519 // move any anchored connected items
520 if (FreeItems.hasItemsAttachedToCursor()) {
521 List<Item> toAdd = new ArrayList<Item>();
522 List<Item> toCheck = new ArrayList<Item>(FreeItems.getInstance());
523
524 while (toCheck.size() > 0) {
525 Item i = toCheck.get(0);
526 Collection<Item> connected = i.getAllConnected();
527
528 // // Only move completely enclosed items
529 // if (!toCheck.containsAll(connected)) {
530 // connected.retainAll(FreeItems.getInstance());
531 // FreeItems.getInstance().removeAll(connected);
532 // toCheck.removeAll(connected);
533 // FrameMouseActions.anchor(connected);
534 // } else {
535 // toCheck.removeAll(connected);
536 // }
537
538 // Anchor overlay items where they belong
539 if (i.getParent() != null && i.getParent() != current) {
540 FreeItems.getInstance().removeAll(connected);
541 toCheck.removeAll(connected);
542 StandardGestureActions.anchor(connected);
543 } else {
544 // Add stuff that is partially enclosed
545 // remove all the connected items from our list to check
546 toCheck.removeAll(connected);
547 // Dont add the items that are free
548 connected.removeAll(FreeItems.getInstance());
549 toAdd.addAll(connected);
550 }
551 }
552
553 current.removeAllItems(toAdd);
554
555 boolean oldChange = toDisplay.hasChanged();
556 toDisplay.updateIDs(toAdd);
557 toDisplay.addAllItems(toAdd);
558 toDisplay.setChanged(oldChange);
559 }
560
561 if (addToBack && current != toDisplay) {
562 FrameIO.checkTDFC(current);
563 }
564
565 // if the saving happened properly, we can continue
566 if (!LeavingFrame(current)) {
567 MessageBay.displayMessage("Navigation cancelled");
568 return;
569 }
570
571 if (addToBack && current != toDisplay) {
572 DisplayController.addToBack(current);
573 }
574
575 Parse(toDisplay);
576
577 if (DisplayController.isAudienceMode()) {
578 // Only need to worry about frame transitions when in Audience Mode
579
580 // Test to see if frame transition specified through annotation, and perform it
581 // if one if found
582 Item frameTransition = getAnnotation(toDisplay, "@frameTransition");
583 if (frameTransition != null) {
584 doFrameTransition(frameTransition, current, toDisplay);
585 }
586 }
587
588 DisplayController.setCurrentFrame(toDisplay, incrementStats);
589 StandardGestureActions.updateCursor();
590 // FrameMouseActions.getInstance().refreshHighlights();
591 // update response timer
592 _LastResponse = ResponseTimer.getElapsedSeconds();
593 _ResponseTimeSum += _LastResponse;
594 DisplayController.updateTitle();
595 }
596
597 /**
598 * Loads and displays the Frame with the given framename, and adds the current
599 * frame to the back-stack if required.
600 *
601 * @param framename
602 * The name of the Frame to load and display
603 * @param addToBack
604 * True if the current Frame should be added to the back-stack, false
605 * otherwise
606 */
607 public static void DisplayFrame(String frameName, boolean addToBack, boolean incrementStats) {
608 Frame newFrame = getFrame(frameName);
609
610 if (newFrame != null) {
611 // display the frame
612 DisplayFrame(newFrame, addToBack, incrementStats);
613 }
614 }
615
616 /**
617 * Loads and displays the Frame with the given framename and adds the current
618 * frame to the back-stack. This is the same as calling DisplayFrame(framename,
619 * true)
620 *
621 * @param framename
622 * The name of the Frame to load and display
623 */
624 public static void DisplayFrame(String framename) {
625 DisplayFrame(framename, true, true);
626 }
627
628 public static Frame getFrame(String frameName) {
629 // if the new frame does not exist then tell the user
630 Frame f = FrameIO.LoadFrame(frameName);
631
632 if (f == null) {
633 MessageBay.errorMessage("Frame '" + frameName + "' could not be found.");
634 }
635
636 return f;
637 }
638
639 /**
640 * Creates a new Picture Item from the given Text source Item and adds it to the
641 * given Frame.
642 *
643 * @return True if the image was created successfully, false otherwise
644 */
645 private static boolean createPicture(Frame frame, Text txt, ItemsList items) {
646 // attempt to create the picture
647 Picture pic = ItemUtils.CreatePicture(txt);
648
649 // if the picture could not be created successfully
650 if (pic == null) {
651 String imagePath = txt.getText();
652 assert (imagePath != null);
653 imagePath = new AttributeValuePair(imagePath).getValue().trim();
654 if (imagePath.length() == 0) {
655 return false;
656 // MessageBay.errorMessage("Expected image path after @i:");
657 } else {
658 MessageBay.errorMessage("Image " + imagePath + " could not be loaded");
659 }
660 return false;
661 }
662 frame.addItem(pic, true, items);
663
664 return true;
665 }
666
667 private static boolean createPictureInBody(Frame frame, Text txt) {
668 return createPicture(frame, txt, frame.getBody());
669 }
670
671 /**
672 * Creates an interactive widget and adds it to a frame. If txt has no parent
673 * the parent will be set to frame.
674 *
675 * @param frame
676 * Frame to add widget to. Must not be null.
677 *
678 * @param txt
679 * Text to create the widget from. Must not be null.
680 *
681 * @return True if created/added. False if could not create.
682 *
683 * @author Brook Novak
684 */
685 private static boolean createWidget(Frame frame, Text txt, ItemsList list) {
686
687 if (frame == null) {
688 throw new NullPointerException("frame");
689 }
690 if (txt == null) {
691 throw new NullPointerException("txt");
692 }
693
694 // Safety
695 if (txt.getParent() == null) {
696 txt.setParent(frame);
697 }
698
699 Widget iw = null;
700
701 try {
702
703 iw = Widget.createWidget(txt);
704
705 } catch (InteractiveWidgetNotAvailableException e) {
706 e.printStackTrace();
707 MessageBay.errorMessage("Cannot create iWidget: " + e.getMessage());
708 } catch (InteractiveWidgetInitialisationFailedException e) {
709 e.printStackTrace();
710 MessageBay.errorMessage("Cannot create iWidget: " + e.getMessage());
711 } catch (IllegalArgumentException e) {
712 e.printStackTrace();
713 MessageBay.errorMessage("Cannot create iWidget: " + e.getMessage());
714 }
715
716 if (iw == null) {
717 return false;
718 }
719
720 frame.removeItem(txt, true, list);
721
722 frame.addAllItems(iw.getItems(), list);
723
724 return true;
725 }
726
727 private static boolean createWidgetInBody(Frame frame, Text txt) {
728 return createWidget(frame, txt, frame.getBody());
729 }
730
731 public static List<String> ParseProfile(Frame profile) {
732 List<String> errors = new LinkedList<String>();
733
734 if (profile == null) {
735 return errors;
736 }
737
738 /*
739 * Make sure the correct cursor shows when turning off the custom cursor and
740 * reparsing the profile frame
741 */
742 FreeItems.getCursor().clear();
743 DisplayController.setCursor(Item.HIDDEN_CURSOR);
744 DisplayController.setCursor(Item.DEFAULT_CURSOR);
745
746 // check for settings tags
747 for (Text item : profile.getBodyTextItems(true)) {
748 try {
749
750 AttributeValuePair avp = new AttributeValuePair(item.getText());
751 String attributeFullCase = avp.getAttributeOrValue();
752
753 if (attributeFullCase == null) {
754 continue;
755 }
756
757 String attribute = attributeFullCase.trim().toLowerCase().replaceAll("^@", "");
758
759 if (attribute.equals("settings")) {
760 Settings.parseSettings(item);
761 }
762
763 } catch (Exception e) {
764 if (e.getMessage() != null) {
765 errors.add(e.getMessage());
766 } else {
767 e.printStackTrace();
768 errors.add("Error parsing [" + item.getText() + "] on " + profile.getName());
769 }
770 }
771 }
772
773 return errors;
774 }
775
776 /**
777 * Sets the first frame to be displayed.
778 *
779 * @param profile
780 */
781 public static void loadFirstFrame(Frame profile) {
782 if (UserSettings.HomeFrame.get() == null) {
783 UserSettings.HomeFrame.set(profile.getName());
784 }
785
786 Frame firstFrame = FrameIO.LoadFrame(UserSettings.HomeFrame.get());
787 if (firstFrame == null) {
788 MessageBay.warningMessage("Home frame not found: " + UserSettings.HomeFrame);
789 UserSettings.HomeFrame.set(profile.getName());
790 DisplayController.setCurrentFrame(profile, true);
791 } else {
792 DisplayController.setCurrentFrame(firstFrame, true);
793 }
794
795 }
796
797 public static Colour[] getColorWheel(Frame frame) {
798 if (frame != null) {
799 List<Text> textItems = frame.getBodyTextItems(false);
800 Colour[] colorList = new Colour[textItems.size() + 1];
801 for (int i = 0; i < textItems.size(); i++) {
802 colorList[i] = textItems.get(i).getColor();
803 }
804 // Make the last item transparency or default for forecolor
805 colorList[colorList.length - 1] = null;
806
807 return colorList;
808 }
809 return new Colour[] { Colour.BLACK, Colour.WHITE, null };
810 }
811
812 public static String getLink(Item item, String alt) {
813 if (item == null || !(item instanceof Text)) {
814 return alt;
815 }
816
817 AttributeValuePair avp = new AttributeValuePair(item.getText());
818 assert (avp != null);
819
820 if (avp.hasPair() && avp.getValue().trim().length() != 0) {
821 item.setLink(avp.getValue());
822 return avp.getValue();
823 } else if (item.getLink() != null) {
824 return item.getAbsoluteLink();
825 }
826
827 return alt;
828 }
829
830 public static String getDir(String name) {
831 if (name != null) {
832 File tester = new File(name);
833 if (tester.exists() && tester.isDirectory()) {
834 if (name.endsWith(File.separator)) {
835 return name;
836 } else {
837 return name + File.separator;
838 }
839 } else {
840 throw new RuntimeException("Directory not found: " + name);
841 }
842 }
843 throw new RuntimeException("Missing value for profile attribute" + name);
844 }
845
846 public static ArrayList<String> getDirs(Item item) {
847 ArrayList<String> dirsToAdd = new ArrayList<String>();
848 String dirListFrameName = item.getAbsoluteLink();
849 if (dirListFrameName != null) {
850 Frame dirListFrame = FrameIO.LoadFrame(dirListFrameName);
851 if (dirListFrame != null) {
852 for (Text t : dirListFrame.getBodyTextItems(false)) {
853 String dirName = t.getText().trim();
854 File tester = new File(dirName);
855 if (tester.exists() && tester.isDirectory()) {
856 if (dirName.endsWith(File.separator)) {
857 dirsToAdd.add(dirName);
858 } else {
859 dirsToAdd.add(dirName + File.separator);
860 }
861 }
862 }
863 }
864 }
865
866 return dirsToAdd;
867 }
868
869 private static void transformOutOfPlaceItems(Frame toParse, ItemsList toTransform) {
870 // Get all items from toTransform that have not been marked as deleted.
871 List<Item> items = toParse.getItems(false, toTransform);
872
873 // if XRayMode is on, replace pictures with their underlying text
874 if (DisplayController.isXRayMode()) {
875
876 // BROOK: Must handle these a little different
877 List<Widget> widgets = toParse.getInteractiveWidgets();
878
879 for (Item i : items) {
880 if (i instanceof XRayable) {
881 toParse.removeItem(i, true, toTransform);
882 // Show the items
883 for (Item item : ((XRayable) i).getConnected()) {
884 item.setVisible(true);
885 item.removeEnclosure(i);
886 }
887 } else if (i instanceof WidgetCorner) {
888 toParse.removeItem(i, true, toTransform);
889 } else if (i instanceof WidgetEdge) {
890 toParse.removeItem(i, true, toTransform);
891 } else if (i.hasFormula()) {
892 i.setText(i.getFormula());
893 } else if (i.hasOverlay()) {
894 i.setVisible(true);
895 // int x = i.getBoundsHeight();
896 }
897 }
898
899 for (Widget iw : widgets) {
900 toParse.addItem(iw.getSource(), true, toTransform);
901 }
902 }
903
904
905 // disable reading of cached overlays if in twinframes mode
906 if (DisplayController.isTwinFramesOn()) {
907 FrameIO.SuspendCache();
908 }
909
910 toParse.clearAnnotations();
911
912 // check for any new overlay items
913 items = toParse.getItems(false, toTransform);
914 for (Item i : items) {
915 try {
916 if (i instanceof WidgetCorner) {
917 // TODO improve efficiency so it only updates once... using
918 // observer design pattern
919 i.update();
920 } else if (i instanceof Text) {
921 if (!DisplayController.isXRayMode() && i.isAnnotation()) {
922 if (ItemUtils.startsWithTag(i, ItemUtils.TAG_IMAGE, true)) {
923 if (!i.hasEnclosures()) {
924 createPicture(toParse, (Text) i, toTransform);
925 }
926 // check for frame images
927 } else if (ItemUtils.startsWithTag(i, ItemUtils.TAG_FRAME_IMAGE) && i.getLink() != null
928 && !i.getAbsoluteLink().equalsIgnoreCase(toParse.getName())) {
929 XRayable image = null;
930 if (i.hasEnclosures()) {
931 // i.setHidden(true);
932 // image =
933 // i.getEnclosures().iterator().next();
934 // image.refresh();
935 } else {
936 image = new FrameImage((Text) i, null);
937 }
938 // TODO Add the image when creating new
939 // FrameImage
940 toParse.addItem(image, true, toTransform);
941 } else if (ItemUtils.startsWithTag(i, ItemUtils.TAG_BITMAP_IMAGE) && i.getLink() != null
942 && !i.getAbsoluteLink().equalsIgnoreCase(toParse.getName())) {
943 XRayable image = null;
944 if (i.hasEnclosures()) {
945 // image =
946 // i.getEnclosures().iterator().next();
947 // image.refresh();
948 // i.setHidden(true);
949 } else {
950 // If a new bitmap is created for a
951 // frame which already has a bitmap dont
952 // recreate the bitmap
953 image = new FrameBitmap((Text) i, null);
954 }
955 toParse.addItem(image, true, toTransform);
956 } else if (ItemUtils.startsWithTag(i, "@c")) {
957 // Can only have a @c
958 if (!i.hasEnclosures() && i.getLines().size() == 1) {
959 Circle circle = new Circle((Text) i);
960 toParse.addItem(circle, true, toTransform);
961 }
962 // Check for JSItem
963 } else if (ItemUtils.startsWithTag(i, "@js")) {
964 JSItem jsItem = new JSItem((Text) i);
965 toParse.addItem(jsItem, true, toTransform);
966 // Check for interactive widgets
967 } else if (ItemUtils.startsWithTag(i, ItemUtils.TAG_IWIDGET)) {
968 createWidget(toParse, (Text) i, toTransform);
969 }
970
971 // TODO decide exactly what to do here!!
972 toParse.addAnnotation((Text) i);
973 } else if (!DisplayController.isXRayMode() && i.hasFormula()) {
974 i.calculate(i.getFormula());
975 }
976 }
977 } catch (Exception e) {
978 Logger.Log(e);
979 e.printStackTrace();
980 System.err
981 .println("**** Have temporarily supressed MessageBay call, as resulted in infinite recursion");
982 // MessageBay.warningMessage("Exception occured when loading " +
983 // i.getClass().getSimpleName() + "(ID: "
984 // + i.getID() + ") " + e.getMessage() != null ? e.getMessage() : "");
985 }
986 }
987
988 /*
989 * for (Item i : items) { if (i instanceof Dot) { ((Dot)
990 * i).setPointType(pointtype); ((Dot) i).useFilledPoints(filledPoints); } }
991 */
992
993 if (DisplayController.isTwinFramesOn()) {
994 FrameIO.ResumeCache();
995 }
996
997
998 }
999
1000 private static void generatingSupportingItems(Frame toParse,
1001 ItemsList toBuildOff, boolean ignoreAnnotations) {
1002 // Get all items from toBuildOff that have not been marked as deleted.
1003 List<Item> items = toParse.getItems(false, toBuildOff);
1004
1005 List<Overlay> overlays = new ArrayList<Overlay>();
1006 List<Vector> vectors = new ArrayList<Vector>();
1007
1008 // disable reading of cached overlays if in twinframes mode
1009 if (DisplayController.isTwinFramesOn()) {
1010 FrameIO.SuspendCache();
1011 }
1012
1013 UserAppliedPermission permission = toParse.getUserAppliedPermission();
1014 // check for any new overlay items
1015 for (Item i : items) {
1016 try {
1017 // reset overlay permission
1018 i.setOverlayPermission(null);
1019 if (i instanceof Text) {
1020 if (i.isAnnotation()) {
1021 if (!DisplayController.isXRayMode() && ItemUtils.startsWithTag(i, ItemUtils.TAG_VECTOR)
1022 && i.getLink() != null) {
1023 if (!i.getAbsoluteLink().equals(toParse.getName())) {
1024 addVector(vectors, UserAppliedPermission.none, permission, i);
1025 }
1026 } else if (!DisplayController.isXRayMode()
1027 && ItemUtils.startsWithTag(i, ItemUtils.TAG_ACTIVE_VECTOR) && i.getLink() != null) {
1028 if (!i.getAbsoluteLink().equals(toParse.getName())) {
1029 addVector(vectors, UserAppliedPermission.followLinks, permission, i);
1030 }
1031 }
1032 // check for new OVERLAY items
1033 else if (!ignoreAnnotations && ItemUtils.startsWithTag(i, ItemUtils.TAG_OVERLAY)
1034 && i.getLink() != null) {
1035 if (i.getAbsoluteLink().equalsIgnoreCase(toParse.getName())) {
1036 // This frame contains an active overlay which
1037 // points to itself
1038 MessageBay.errorMessage(toParse.getName() + " contains an @o which links to itself");
1039 continue;
1040 }
1041
1042 Frame overlayFrame = FrameIO.LoadFrame(i.getAbsoluteLink());
1043 // Parse(overlay);
1044 if (overlayFrame != null && Overlay.getOverlay(overlays, overlayFrame) == null) {
1045 overlays.add(new Overlay(overlayFrame, UserAppliedPermission.none));
1046 }
1047 }
1048 // check for ACTIVE_OVERLAY items
1049 else if (!ignoreAnnotations && ItemUtils.startsWithTag(i, ItemUtils.TAG_ACTIVE_OVERLAY)
1050 && i.getLink() != null) {
1051 String link = i.getAbsoluteLink();
1052 if (link.equalsIgnoreCase(toParse.getName())) {
1053 // This frame contains an active overlay which
1054 // points to itself
1055 MessageBay.errorMessage(toParse.getName() + " contains an @ao which links to itself");
1056 continue;
1057 }
1058 Frame overlayFrame = null;
1059
1060 Frame current = DisplayController.getCurrentFrame();
1061 if (current != null) {
1062 for (Overlay o : current.getOverlays()) {
1063 if (o.Frame.getName().equalsIgnoreCase(link)) {
1064 overlayFrame = o.Frame;
1065 }
1066 }
1067 }
1068 if (overlayFrame == null) {
1069 overlayFrame = FrameIO.LoadFrame(link);
1070 }
1071
1072 // get level if specified
1073 String level = new AttributeValuePair(i.getText()).getValue();
1074 // default permission (if none is specified)
1075 PermissionTriple permissionLevel = new PermissionTriple(level,
1076 UserAppliedPermission.followLinks);
1077
1078 if (overlayFrame != null) {
1079 Overlay existingOverlay = Overlay.getOverlay(overlays, overlayFrame);
1080 // If it wasn't in the list create it and add
1081 // it.
1082 if (existingOverlay == null) {
1083 Overlay newOverlay = new Overlay(overlayFrame,
1084 permissionLevel.getPermission(overlayFrame.getOwner(), overlayFrame.getGroupMembers()));
1085 i.setOverlay(newOverlay);
1086 overlays.add(newOverlay);
1087 } else {
1088 existingOverlay.Frame.setPermission(permissionLevel);
1089 }
1090 }
1091 }
1092
1093 }
1094 }
1095 } catch (Exception e) {
1096 Logger.Log(e);
1097 e.printStackTrace();
1098 System.err.println("**** Have temporarily supressed MessageBay call, as resulted in infinite recursion");
1099 //MessageBay.warningMessage("Exception occured when loading " + i.getClass().getSimpleName() + "(ID: "
1100 // + i.getID() + ") " + e.getMessage() != null ? e.getMessage() : "");
1101 }
1102 }
1103
1104 /*
1105 * for (Item i : items) { if (i instanceof Dot) { ((Dot)
1106 * i).setPointType(pointtype); ((Dot) i).useFilledPoints(filledPoints); } }
1107 */
1108
1109 if (DisplayController.isTwinFramesOn()) {
1110 FrameIO.ResumeCache();
1111 }
1112
1113 toParse.clearOverlays();
1114 toParse.clearVectors();
1115 toParse.addAllOverlays(overlays);
1116 toParse.addAllVectors(vectors);
1117 }
1118
1119 public static void Parse(Frame toParse) {
1120 Parse(toParse, false);
1121 }
1122
1123 /**
1124 * Checks for any special Annotation items and updates the display as necessary.
1125 * Special Items: Images, overlays, sort.
1126 *
1127 */
1128 public static void Parse(Frame toParse, boolean firstParse) {
1129 Parse(toParse, firstParse, false);
1130 }
1131
1132 /**
1133 *
1134 * @param toParse
1135 * @param firstParse
1136 * @param ignoreAnnotations
1137 * used to prevent infinate loops such as when performing TDFC with
1138 * an ao tag linked to a frame with an frameImage of a frame which
1139 * also has an ao tag on it.
1140 */
1141 public static void Parse(Frame toParse, boolean firstParse, boolean ignoreAnnotations) {
1142 List<String> accessList = Label.getAccessibleLabelsNames(new ItemsList(toParse.getPrimaryBody()));
1143
1144 ItemsList primaries = toParse.getPrimaryBody();
1145 ItemsList surrogates = toParse.getSurrogateBody();
1146
1147 transformOutOfPlaceItems(toParse, primaries);
1148 transformOutOfPlaceItems(toParse, surrogates);
1149
1150 toParse.getInteractableItems().clear();
1151 List<Item> newBody = parseFromPrimary(primaries, accessList);
1152 toParse.getBody().clear();
1153 toParse.getBody().addAll(newBody);
1154 generatingSupportingItems(toParse, toParse.getBody(), ignoreAnnotations);
1155
1156 if (firstParse) {
1157 ItemUtils.EnclosedCheck(toParse.getSortedItems());
1158 }
1159 }
1160
1161 private static List<Item> parseFromPrimary(ItemsList primaryBody, List<String> access) {
1162 List<Item> parsedBody = new ArrayList<Item>();
1163
1164 for (Item item: primaryBody) {
1165 String encryptionLabel = item.getEncryptionLabel();
1166 if (encryptionLabel == null || encryptionLabel.isEmpty()) {
1167 parsedBody.add(item);
1168 } else if (access.contains(encryptionLabel)) {
1169 parsedBody.add(item);
1170 } else {
1171 parsedBody.addAll(item.getSurrogates());
1172 }
1173 }
1174
1175 return parsedBody;
1176 }
1177
1178 /**
1179 * TODO: Comment. cts16
1180 *
1181 * @param vectors
1182 * @param permission
1183 * @param i
1184 */
1185 private static void addVector(List<Vector> vectors, UserAppliedPermission defaultPermission,
1186 UserAppliedPermission framePermission, Item i) {
1187 // TODO It is possible to get into an infinite loop if a
1188 // frame contains an @ao which leads to a frame with an
1189 // @v which points back to the frame with the @ao
1190 Frame vector = FrameIO.LoadFrame(i.getAbsoluteLink());
1191
1192 // Get the permission from off the vector frame
1193 UserAppliedPermission vectorPermission = UserAppliedPermission
1194 .getPermission(vector.getAnnotationValue("permission"), defaultPermission);
1195
1196 // If the frame permission is lower, use that
1197 vectorPermission = UserAppliedPermission.min(vectorPermission, framePermission);
1198
1199 // Highest permissable permission for vectors is copy
1200 vectorPermission = UserAppliedPermission.min(vectorPermission, UserAppliedPermission.copy);
1201 if (vector != null) {
1202 String scaleString = new AttributeValuePair(i.getText()).getValue();
1203 Float scale = 1F;
1204 try {
1205 scale = Float.parseFloat(scaleString);
1206 } catch (Exception e) {
1207 }
1208 Vector newVector = new Vector(vector, vectorPermission, scale, i);
1209 i.setOverlay(newVector);
1210 i.setVisible(false);
1211 vectors.add(newVector);
1212 }
1213 }
1214
1215 public static Item onItem(float floatX, float floatY, boolean changeLastEdited) {
1216 return onItem(DisplayController.getCurrentFrame(), floatX, floatY, changeLastEdited);
1217 }
1218
1219 /**
1220 * Searches through the list of items on this frame to find one at the given x,y
1221 * coordinates.
1222 *
1223 * @param x
1224 * The x coordinate
1225 * @param y
1226 * The y coordinate
1227 * @return The Item at the given coordinates, or NULL if none is found.
1228 */
1229 public static Item onItem(Frame toCheck, float floatX, float floatY, boolean bResetLastEdited) {
1230 // System.out.println("MouseX: " + floatX + " MouseY: " + floatY);
1231 int x = Math.round(floatX);
1232 int y = Math.round(floatY);
1233 if (toCheck == null) {
1234 return null;
1235 }
1236
1237 List<Item> possibles = new ArrayList<Item>(0);
1238
1239 // if the mouse is in the message area
1240 if (y >= DisplayController.getMessageBayPaintArea().getMinY()) {
1241 // check the individual bay items (MessageBay + MailBay)
1242 List<Item> bayItems = new LinkedList<Item>();
1243 if (DisplayController.isMailMode()) {
1244 bayItems.addAll(MailBay.getPreviewMessages());
1245 } else {
1246 bayItems.addAll(MessageBay.getMessages());
1247 }
1248 for (Item message : bayItems) {
1249 if (message != null) {
1250 if (message.contains(new Point(x, y))) {
1251 message.setOverlayPermission(UserAppliedPermission.copy);
1252 possibles.add(message);
1253 } else {
1254 // Not sure why but if the line below is removed then
1255 // several items can be highlighted at once
1256 message.setHighlightMode(Item.HighlightMode.None);
1257 message.setHighlightColorToDefault();
1258 }
1259 }
1260 }
1261
1262 // check the link to the message/mail frame
1263 Item linkItem = DisplayController.isMailMode() ? MailBay.getMailLink() : MessageBay.getMessageLink();
1264 if (linkItem != null && linkItem.contains(new Point(x, y))) {
1265 linkItem.setOverlayPermission(UserAppliedPermission.copy);
1266 possibles.add(linkItem);
1267 }
1268
1269 // this is taken into account in contains
1270 // y -= FrameGraphics.getMaxFrameSize().height;
1271 // otherwise, the mouse is on the frame
1272 } else {
1273 if (LastEdited != null) {
1274 if (LastEdited.contains(x, y) && !FreeItems.getInstance().contains(LastEdited)
1275 && LastEdited.getParent() == DisplayController.getCurrentFrame()
1276 && LastEdited.getParent().getSortedItems().contains(LastEdited)) {
1277 LastEdited.setOverlayPermission(UserAppliedPermission.full);
1278 return LastEdited;
1279 } else if (bResetLastEdited) {
1280 setLastEdited(null);
1281 }
1282 }
1283 ArrayList<Item> checkList = new ArrayList<Item>();
1284 checkList.addAll(toCheck.getInteractableItems());
1285 checkList.add(toCheck.getNameItem());
1286
1287 for (Item i : checkList) {
1288
1289 // do not check annotation items in audience mode
1290 // TODO: Upon hover of Rubbish Bin, Undo and Restore Widgets, flickering occurs
1291 // depending on the mouse distance from a corner. Resolve this.
1292 if (i.isVisible() && !(DisplayController.isAudienceMode() && i.isAnnotation())) {
1293 if (i instanceof WidgetCorner) {
1294 WidgetCorner wc = (WidgetCorner) i;
1295 if (wc.getWidgetSource() instanceof ButtonWidget) {
1296 ButtonWidget bw = (ButtonWidget) wc.getWidgetSource();
1297
1298 if (bw.getdropInteractableStatus() == true) {
1299 Widget iw = wc.getWidgetSource();
1300
1301 if (iw.getBounds().contains(x, y)) {
1302
1303 if (!FreeItems.getInstance().contains(i)) {
1304 possibles.add(i);
1305 }
1306 }
1307 }
1308 }
1309 }
1310
1311 if (i.contains(new Point(x, y))) {
1312 if (!FreeItems.getInstance().contains(i)) {
1313 possibles.add(i);
1314 }
1315 }
1316
1317 }
1318 }
1319 }
1320
1321 // if there are no possible items, return null
1322 if (possibles.size() == 0) {
1323 return null;
1324 }
1325
1326 // if there is only one possibility, return it
1327 if (possibles.size() == 1) {
1328 return possibles.get(0);
1329 }
1330
1331 // return closest x,y pair to mouse
1332 Item closest = possibles.get(0);
1333 int distance = (int) Math.round(
1334 Math.sqrt(Math.pow(Math.abs(closest.getX() - x), 2) + Math.pow(Math.abs(closest.getY() - y), 2)));
1335
1336 for (Item i : possibles) {
1337 int d = (int) Math
1338 .round(Math.sqrt(Math.pow(Math.abs(i.getX() - x), 2) + Math.pow(Math.abs(i.getY() - y), 2)));
1339
1340 // System.out.println(d);
1341 if (d <= distance) {
1342 distance = d;
1343
1344 // dots take precedence over lines
1345 if ((!(closest instanceof Dot && i instanceof Line))
1346 && (!(closest instanceof Text && i instanceof Line))) {
1347 closest = i;
1348 }
1349
1350 }
1351
1352 }
1353
1354 return closest;
1355 }
1356
1357 /**
1358 * Checks if the mouse is currently over an item.
1359 *
1360 * @return True if the mouse is over any item, false otherwise.
1361 */
1362 public static boolean hasCurrentItem() {
1363 return getCurrentItem() != null;
1364 }
1365
1366 public synchronized static Item getCurrentItem() {
1367 return onItem(DisplayController.getCurrentFrame(), DisplayController.getMouseX(), DisplayController.getMouseY(),
1368 true);
1369 }
1370
1371 public static PolygonBounds getEnlosingPolygon() {
1372 Collection<Item> enclosure = getEnclosingLineEnds();
1373
1374 if (enclosure == null || enclosure.size() == 0) {
1375 return null;
1376 }
1377
1378 return enclosure.iterator().next().getEnclosedShape();
1379 }
1380
1381 /**
1382 *
1383 * @param currentItem
1384 * @return
1385 */
1386 public static Collection<Item> getCurrentItems() {
1387 return getCurrentItems(getCurrentItem());
1388 }
1389
1390 public static Collection<Item> getCurrentItems(Item currentItem) {
1391 Collection<Item> enclosure = getEnclosingLineEnds();
1392
1393 if (enclosure == null || enclosure.size() == 0) {
1394 return null;
1395 }
1396
1397 Item firstItem = enclosure.iterator().next();
1398
1399 Collection<Item> enclosed = getItemsEnclosedBy(DisplayController.getCurrentFrame(),
1400 firstItem.getEnclosedShape());
1401
1402 // Brook: enclosed widgets are to be fully enclosed, never partially
1403 /*
1404 * MIKE says: but doesn't this mean that widgets are treated differently from
1405 * ALL other object which only need to be partially enclosed to be picked up
1406 */
1407 List<Widget> enclosedWidgets = new LinkedList<Widget>();
1408 for (Item i : enclosed) {
1409 // Don't want to lose the highlighting from the current item
1410 if (i == currentItem || enclosure.contains(i)) {
1411 continue;
1412 }
1413 // Don't want to lose the highlighting of connected Dots
1414 // TODO: this code does nothing (perhaps the continue is meant for the outer
1415 // for loop?). cts16
1416 if (i instanceof Dot && i.getHighlightMode() == HighlightMode.Connected) {
1417 for (Line l : i.getLines()) {
1418 if (l.getOppositeEnd(i).getHighlightMode() == HighlightMode.Normal) {
1419 continue;
1420 }
1421 }
1422 }
1423
1424 if (i instanceof WidgetCorner) {
1425 if (!enclosedWidgets.contains(((WidgetCorner) i).getWidgetSource())) {
1426 enclosedWidgets.add(((WidgetCorner) i).getWidgetSource());
1427 }
1428 }
1429
1430 i.setHighlightMode(Item.HighlightMode.None);
1431 i.setHighlightColorToDefault();
1432 }
1433
1434 for (Widget iw : enclosedWidgets) {
1435 for (Item i : iw.getItems()) {
1436 if (!enclosed.contains(i)) {
1437 enclosed.add(i);
1438 }
1439 }
1440 }
1441
1442 return enclosed;
1443 }
1444
1445 /**
1446 * Gets the collection of Dot items that form the enclosure nearest to the
1447 * current mouse position.
1448 */
1449 public static Collection<Item> getEnclosingLineEnds() {
1450 return getEnclosingLineEnds(new Point(DisplayController.getMouseX(), DisplayController.getMouseY()));
1451 }
1452
1453 /**
1454 * Gets the collection of Dot items that form the enclosure nearest to the given
1455 * position.
1456 */
1457 public static Collection<Item> getEnclosingLineEnds(Point position) {
1458 // update enclosed shapes
1459 Frame current = DisplayController.getCurrentFrame();
1460 if (current == null) {
1461 return null;
1462 }
1463 List<Item> items = current.getSortedItems();
1464
1465 // Remove all items that are connected to freeItems
1466 List<Item> freeItems = new ArrayList<Item>(FreeItems.getInstance());
1467 while (freeItems.size() > 0) {
1468 Item item = freeItems.get(0);
1469 Collection<Item> connected = item.getAllConnected();
1470 items.removeAll(connected);
1471 freeItems.removeAll(connected);
1472 }
1473
1474 List<Item> used = new ArrayList<Item>(0);
1475
1476 while (items.size() > 0) {
1477 Item i = items.get(0);
1478 items.remove(i);
1479 if (i.isEnclosed()) {
1480 PolygonBounds p = i.getEnclosedShape();
1481 if (p.contains(position)) {
1482 used.add(i);
1483 items.removeAll(i.getEnclosingDots());
1484 }
1485 }
1486 }
1487
1488 if (used.size() == 0) {
1489 return null;
1490 }
1491
1492 // if there is only one possibility, return it
1493 if (used.size() == 1) {
1494 return used.get(0).getEnclosingDots();
1495 // otherwise, determine which polygon is closest to the cursor
1496 } else {
1497 Collections.sort(used, new Comparator<Item>() {
1498 @Override
1499 public int compare(Item d1, Item d2) {
1500 PolygonBounds p1 = d1.getEnclosedShape();
1501 PolygonBounds p2 = d2.getEnclosedShape();
1502
1503 int closest = Integer.MAX_VALUE;
1504 int close2 = Integer.MAX_VALUE;
1505
1506 int mouseX = DisplayController.getMouseX();
1507 int mouseY = DisplayController.getMouseY();
1508
1509 for (int i = 0; i < p1.getPointCount(); i++) {
1510 int diff = Math.abs(p1.getPoint(i).getX() - mouseX) + Math.abs(p1.getPoint(i).getY() - mouseY);
1511 int diff2 = Integer.MAX_VALUE;
1512
1513 if (i < p2.getPointCount()) {
1514 diff2 = Math.abs(p2.getPoint(i).getX() - mouseX) + Math.abs(p2.getPoint(i).getY() - mouseY);
1515 }
1516
1517 if (diff < Math.abs(closest)) {
1518 close2 = closest;
1519 closest = diff;
1520 } else if (diff < Math.abs(close2)) {
1521 close2 = diff;
1522 }
1523
1524 if (diff2 < Math.abs(closest)) {
1525 close2 = closest;
1526 closest = -diff2;
1527 } else if (diff2 < Math.abs(close2)) {
1528 close2 = diff2;
1529 }
1530 }
1531
1532 if (closest > 0 && close2 > 0) {
1533 return -10;
1534 }
1535
1536 if (closest < 0 && close2 < 0) {
1537 return 10;
1538 }
1539
1540 if (closest > 0) {
1541 return -10;
1542 }
1543
1544 return 10;
1545 }
1546
1547 });
1548
1549 return used.get(0).getEnclosingDots();
1550 }
1551 }
1552
1553 // TODO Remove this method!!
1554 // Can just getItemsWithin be used?
1555 public static Collection<Item> getItemsEnclosedBy(Frame frame, PolygonBounds poly) {
1556 Collection<Item> contained = frame.getItemsWithin(poly);
1557
1558 Collection<Item> results = new LinkedHashSet<Item>(contained.size());
1559
1560 // check for correct permissions
1561 for (Item item : contained) {
1562 // if the item is on the frame
1563 if (item.getParent() == frame || item.getParent() == null) {
1564 // item.Permission = Permission.full;
1565 results.add(item);
1566 // otherwise, it must be on an overlay frame
1567 } else {
1568 for (Overlay overlay : frame.getOverlays()) {
1569 if (overlay.Frame == item.getParent()) {
1570 item.setOverlayPermission(overlay.permission);
1571 results.add(item);
1572 break;
1573 }
1574 }
1575 }
1576 }
1577
1578 return results;
1579 }
1580
1581 public static void CreateDefaultProfile(String profileFor, Frame profile) {
1582 CreateDefaultProfile(profileFor, profile, null, null);
1583 }
1584
1585 /**
1586 * Copies the content from the default profile to the specified profile.
1587 * @param profileFor Name of profile that is destination of copy.
1588 * @param profile Profile being setup.
1589 * @param specifiedTextSettings text settings to provide a default value for in the new profile
1590 * @param specifiedGenericSettings generic settings to provide a default value for in the new profile
1591 */
1592 public static void CreateDefaultProfile(String profileFor, Frame profile,
1593 Map<String, Setting> specifiedSettings, Map<String, Consumer<Frame>> notifyWhenGenerated) {
1594 // If this is already the default profile then nothing (other than setting
1595 // title) needs to be done.
1596 Text titleItem = profile.getTitleItem();
1597 Text title = titleItem;
1598 if (!profileFor.equals(UserSettings.DEFAULT_PROFILE_NAME)) {
1599 // If this profile is not the default profile, copy it from the default profile
1600 // instead of generating a new profile
1601 // (this allows the possibility of modifying the default profile and having any
1602 // new profiles get those modifications)
1603 Frame defaultFrame = FrameIO.LoadProfile(UserSettings.DEFAULT_PROFILE_NAME);
1604 if (defaultFrame == null) {
1605 try {
1606 // If we do not have a default to copy, create one.
1607 defaultFrame = FrameIO.CreateNewProfile(UserSettings.DEFAULT_PROFILE_NAME, null, null);
1608 } catch (InvalidFramesetNameException invalidNameEx) {
1609 MessageBay.errorMessage("Failed to create default profile named: "
1610 + UserSettings.DEFAULT_PROFILE_NAME + ". "
1611 + "Profile names must start and end with a letter and must contain only letters and numbers.");
1612 return;
1613 } catch (ExistingFramesetException existingFramesetEx) {
1614 MessageBay.errorMessage("Failed to create the desired default frameset: "
1615 + UserSettings.DEFAULT_PROFILE_NAME + ", "
1616 + "because it already exists. This should never happen as we shouldn't be asking to create it if it already exists.");
1617 return;
1618 }
1619 }
1620
1621 MessageBay.suppressMessages(true);
1622 int lastNumber = FrameIO.getLastNumber(defaultFrame.getFramesetName());
1623 for (int i = 1; i <= lastNumber; i++) {
1624 // Load in next default, if it doesn't exist continue loop.
1625 defaultFrame = FrameIO.LoadFrame(defaultFrame.getFramesetName() + i);
1626 if (defaultFrame == null) {
1627 continue;
1628 }
1629
1630 // Create the next next (currently blank) profile frame.
1631 // If there is frame gaps in the default (say if there is no 4.exp but there is
1632 // a 5.exp) then retain those gaps.
1633 // This way copied relative links work.
1634 while (profile.getNumber() < defaultFrame.getNumber()) {
1635 profile = FrameIO.CreateFrame(profile.getFramesetName(), null, null);
1636 }
1637 // Ensure we are working from a blank slate.
1638 profile.reset();
1639 profile.removeAllItems(profile.getAllItems());
1640
1641 // For each item on defaultFrame:
1642 // 1. Set all items to be relatively linked so once copied their links correctly
1643 // point to the frame on the created profile rather than the default profile.
1644 // 2. Copy item from defaultFrame to the current profile frame being
1645 // constructed.
1646 // 3. Replace settings values of copied items with those specified in
1647 // specifiedSettings (if present)
1648 for (Item item : defaultFrame.getAllItems()) {
1649 item.setRelativeLink();
1650 }
1651 profile.addAllItems(defaultFrame.getAllItems());
1652 if (i == 1 && titleItem != null) {
1653 titleItem.setText(profileFor + "'s Profile");
1654 }
1655 String category = profile.getTitle();
1656 List<String> settingsKeys = null;
1657 if (specifiedSettings != null) {
1658 settingsKeys = specifiedSettings.keySet().stream().filter(key ->
1659 key.startsWith(category)).collect(Collectors.toList());
1660 }
1661 if (settingsKeys != null) {
1662 for (String key: settingsKeys) {
1663 Setting setting = specifiedSettings.get(key);
1664 String name = setting.getName();
1665 Text representation = setting.generateRepresentation(name, profile.getFramesetName());
1666 Collection<Text> canditates = profile.getTextItems();
1667 canditates.removeIf(text -> !text.getText().startsWith(representation.getText().split(" ")[0]));
1668 canditates.forEach(text -> {
1669 Point backupPos = text.getPosition();
1670 Item.DuplicateItem(representation, text);
1671 text.setText(representation.getText());
1672 text.setPosition(backupPos);
1673 });
1674 }
1675 }
1676 if (notifyWhenGenerated != null && notifyWhenGenerated.containsKey(category)) {
1677 notifyWhenGenerated.get(category).accept(profile);
1678 }
1679 FrameIO.SaveFrame(profile);
1680 }
1681 MessageBay.suppressMessages(false);
1682 } else {
1683 title.setText("Default Profile Frame");
1684 int xPos = 300;
1685 int yPos = 100;
1686 Text t;
1687
1688 // Add documentation links
1689 File helpDirectory = new File(FrameIO.HELP_PATH);
1690 if (helpDirectory != null) {
1691 File[] helpFramesets = helpDirectory.listFiles();
1692 if (helpFramesets != null) {
1693
1694 // Add the title for the help index
1695 Text help = profile.addText(xPos, yPos, "@Expeditee Help", null);
1696 help.setSize(25);
1697 help.setFontStyle("Bold");
1698 help.setFamily("SansSerif");
1699 help.setColor(TemplateSettings.ColorWheel.get()[3]);
1700
1701 xPos += 25;
1702 System.out.println("Installing frameset: ");
1703
1704 boolean first_item = true;
1705 for (File helpFrameset : helpFramesets) {
1706 String framesetName = helpFrameset.getName();
1707 if (!FrameIO.isValidFramesetName(framesetName)) {
1708 continue;
1709 }
1710
1711 if (first_item) {
1712 System.out.print(" " + framesetName);
1713 first_item = false;
1714 } else {
1715 System.out.print(", " + framesetName);
1716 }
1717 System.out.flush();
1718
1719 Frame indexFrame = FrameIO.LoadFrame(framesetName + '1');
1720 // Look through the folder for help index pages
1721 if (indexFrame != null && ItemUtils.FindTag(indexFrame.getSortedItems(), "@HelpIndex") != null) {
1722 // yPos += spacing;
1723 yPos += 30;
1724 t = profile.addText(xPos, yPos, '@' + indexFrame.getFramesetName(), null);
1725 t.setLink(indexFrame.getName());
1726 t.setColor(Colour.GREY);
1727 }
1728 }
1729 System.out.println();
1730 }
1731 }
1732
1733 xPos = 50;
1734 yPos = 100;
1735
1736 // Populate Start Pages and Settings
1737 File framesetDirectory = new File(FrameIO.FRAME_PATH);
1738
1739 if (framesetDirectory.exists()) {
1740 File[] startpagesFramesets = framesetDirectory.listFiles();
1741
1742 if (startpagesFramesets != null) {
1743 // Add Start Page title
1744 Text templates = profile.addText(xPos, yPos, "@Start Pages", null);
1745 templates.setSize(25);
1746 templates.setFontStyle("Bold");
1747 templates.setFamily("SansSerif");
1748 templates.setColor(TemplateSettings.ColorWheel.get()[3]);
1749
1750 xPos += 25;
1751
1752 // Start Pages should be the first frame in its own frameset +
1753 // frameset name should be present in FrameUtils.startPages[].
1754 for (File startpagesFrameset : startpagesFramesets) {
1755 String framesetName = startpagesFrameset.getName();
1756
1757 // Only add link if frameset is a startpage
1758 for (int i = 0; i < startPages.length; i++) {
1759 if (framesetName.equals(startPages[i])) {
1760 Frame indexFrame = FrameIO.LoadFrame(framesetName + '1');
1761
1762 // Add start page link
1763 if (indexFrame != null) {
1764 yPos += 30;
1765 t = profile.addText(xPos, yPos, '@' + indexFrame.getFramesetName(), null);
1766 t.setLink(indexFrame.getName());
1767 t.setColor(Colour.GREY);
1768 }
1769 }
1770 }
1771 }
1772 }
1773 }
1774
1775 FrameIO.SaveFrame(profile);
1776
1777 // Populate settings frameset
1778 Settings.Init();
1779 t = profile.addText(550, 100, "@Settings", null);
1780 t.setSize((float) 25.0);
1781 t.setFamily("SansSerif");
1782 t.setFontStyle("Bold");
1783 t.setColor(Colour.GREY);
1784 Settings.generateSettingsTree(t);
1785 System.out.println("@Settings: Default settings generation complete.");
1786
1787 FrameIO.SaveFrame(profile);
1788 }
1789 }
1790
1791 private static void checkTDFCItemWaiting(Frame currentFrame) {
1792 Item tdfcItem = FrameUtils.getTdfcItem();
1793 // if there is a TDFC Item waiting
1794 if (tdfcItem != null) {
1795 boolean change = currentFrame.hasChanged();
1796 boolean saved = currentFrame.isSaved();
1797 // Save the parent of the item if it has not been saved
1798 if (!change && !saved) {
1799 tdfcItem.setLink(null);
1800 tdfcItem.getParent().setChanged(true);
1801 FrameIO.SaveFrame(tdfcItem.getParent());
1802 DisplayController.requestRefresh(true);
1803 } else {
1804 SessionStats.CreatedFrame();
1805 }
1806
1807 setTdfcItem(null);
1808 }
1809 }
1810
1811 public static void setTdfcItem(Item _tdfcItem) {
1812 FrameUtils._tdfcItem = _tdfcItem;
1813 }
1814
1815 public static Item getTdfcItem() {
1816 return FrameUtils._tdfcItem;
1817 }
1818
1819 public static void setLastEdited(Text lastEdited) {
1820 // If the lastEdited is being changed then check if its @i
1821 Frame toReparse = null;
1822 Frame toRecalculate = null;
1823 Frame toUpdateObservers = null;
1824
1825 if (LastEdited == null) {
1826 // System.out.print("N");
1827 } else if (LastEdited != null) {
1828 // System.out.print("T");
1829 Frame parent = LastEdited.getParentOrCurrentFrame();
1830
1831 if (lastEdited != LastEdited) {
1832 if (LastEdited.startsWith("@i")) {
1833 // Check if its an image that can be resized to fit a box
1834 // around it
1835 String text = LastEdited.getText();
1836 if (text.startsWith("@i:") && !Character.isDigit(text.charAt(text.length() - 1))) {
1837 Collection<Item> enclosure = FrameUtils.getEnclosingLineEnds(LastEdited.getPosition());
1838 if (enclosure != null) {
1839 for (Item i : enclosure) {
1840 if (i.isLineEnd() && i.isEnclosed()) {
1841 DisplayController.getCurrentFrame().removeAllItems(enclosure);
1842 AxisAlignedBoxBounds rect = i.getEnclosedBox();
1843 LastEdited.setText(LastEdited.getText() + " " + Math.round(rect.getWidth()));
1844 LastEdited.setPosition(rect.getTopLeft());
1845 LastEdited.setThickness(i.getThickness());
1846 LastEdited.setBorderColor(i.getColor());
1847 break;
1848 }
1849 }
1850 StandardGestureActions.deleteItems(enclosure, false);
1851 }
1852 }
1853 toReparse = parent;
1854 } else if (LastEdited.recalculateWhenChanged()) {
1855 toRecalculate = parent;
1856 }
1857
1858 if (parent.hasObservers()) {
1859 toUpdateObservers = parent;
1860 }
1861 // Update the formula if in XRay mode
1862 if (DisplayController.isXRayMode() && LastEdited.hasFormula()) {
1863 LastEdited.setFormula(LastEdited.getText());
1864 }
1865 }
1866 if (lastEdited != LastEdited && LastEdited.getText().length() == 0 && LastEdited.getMinWidth() == null) {
1867 parent.removeItem(LastEdited);
1868 }
1869 }
1870 LastEdited = lastEdited;
1871
1872 if (!DisplayController.isXRayMode()) {
1873 if (toReparse != null) {
1874 Parse(toReparse, false, false);
1875 } else {
1876 if (toRecalculate != null) {
1877 toRecalculate.recalculate();
1878 }
1879
1880 if (toUpdateObservers != null) {
1881 toUpdateObservers.notifyObservers(false);
1882 }
1883 }
1884 }
1885 }
1886
1887 /**
1888 * Extracts files/folders from assets/resources-public and assets/resources-private
1889 * to {Expeditee Home}/resources-public and {Expeditee Home}/resources-private respectively.
1890 * @param force if true, resources will be extracted even ifthey have already been extracted before.
1891 */
1892 public static void extractResources(boolean force) {
1893 // Ensure groups area exists
1894 if (UserSettings.PublicAndPrivateResources) {
1895 // Extract private resources
1896 Path resourcesPrivate = Paths.get(FrameIO.PARENT_FOLDER).resolve("resources-private");
1897 extractResources("org/expeditee/assets/resources-private", resourcesPrivate, force);
1898
1899 // Extract public resources
1900 Path resourcesPublic = Paths.get(FrameIO.PARENT_FOLDER).resolve("resources-public");
1901 extractResources("org/expeditee/assets/resources-public", resourcesPublic, force);
1902 } else if (AuthenticatorBrowser.isAuthenticationRequired()) {
1903 // Deal with the instance of being in the old regime but using authentication.
1904
1905 // Ensure additional framesets
1906 Path framesetsDir = Paths.get(FrameIO.FRAME_PATH);
1907 boolean extracted = extractResources("org/expeditee/assets/resources-public/framesets", framesetsDir, force);
1908
1909 // Ensure additional images
1910 Path imagesDir = Paths.get(FrameIO.IMAGES_PATH);
1911 extracted |= extractResources("org/expeditee/assets/resources-public/images", imagesDir, force);
1912
1913 // Ensure deaddrops area exists
1914 extracted |= Paths.get(FrameIO.PARENT_FOLDER).resolve("deaddrops").toFile().mkdir();
1915
1916 if (extracted) {
1917 @SuppressWarnings("resource")
1918 Scanner in = new Scanner(System.in);
1919 System.out.println("Extracting resources...In order to use authentication, you need a new default profile frameset.");
1920 System.out.println("This is a destructive process, your existing default profile frameset (if it exists) will be deleted.");
1921 System.out.print("Do you want to proceed? y/N:");
1922 System.out.flush();
1923 String answer = in.nextLine();
1924 if (!answer.toLowerCase().startsWith("y")) {
1925 System.out.println("Exiting...");
1926 System.exit(1);
1927 }
1928
1929 // Ensure the default profile is 'update to date'.
1930 //NB: this limits the potential for those running old regime with authentication the ability to customise the default profile.
1931 Path defaultProfile = Paths.get(FrameIO.PROFILE_PATH).resolve("default");
1932 try {
1933 Files.walkFileTree(defaultProfile, new FileVisitor<Path>() {
1934 @Override
1935 public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
1936 dir.toFile().delete();
1937 return FileVisitResult.CONTINUE;
1938 }
1939 @Override
1940 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
1941 return FileVisitResult.CONTINUE;
1942 }
1943 @Override
1944 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
1945 file.toFile().delete();
1946 return FileVisitResult.CONTINUE;
1947 }
1948 @Override
1949 public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
1950 return FileVisitResult.CONTINUE;
1951 }
1952 });
1953 } catch (IOException e) {
1954 e.printStackTrace();
1955 }
1956 }
1957 }
1958 }
1959
1960 private static boolean extractResources(String source, Path destination, boolean force) {
1961 // If resources have already been extracted, and we are not forcing the extraction, there is nothing to do.
1962 if (!force && destination.resolve(".res").toFile().exists()) {
1963 return false;
1964 }
1965
1966 System.out.println("Extracting/Installing resources to: " + destination.getFileName());
1967
1968 // Create the destination
1969 destination.getParent().toFile().mkdirs();
1970
1971 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
1972 URL resourceUrl = classLoader.getResource(source);
1973 if (resourceUrl.getProtocol().equals("jar")) {
1974 try {
1975 JarURLConnection ju_connection = (JarURLConnection) resourceUrl.openConnection();
1976 JarFile jf = ju_connection.getJarFile();
1977 Enumeration<JarEntry> jarEntries = jf.entries();
1978 extractFromJarFile(classLoader, jarEntries, source, destination);
1979 } catch (IOException e) {
1980 System.err.println("Error: FrameUtils::extractResources. Exception whilst extracting resources from Jar File. Message: " + e.getMessage());
1981 }
1982 } else if (resourceUrl.getProtocol().equals("bundleresource")) {
1983 try {
1984 URLConnection urlConnection = resourceUrl.openConnection();
1985 Class<?> c = urlConnection.getClass();
1986 java.lang.reflect.Method toInvoke = c.getMethod("getFileURL");
1987 URL fileURL = (URL) toInvoke.invoke(urlConnection);
1988 extractResourcesFromFolder(new File(fileURL.getPath()), source, destination);
1989 } catch (IOException e) {
1990 System.err.println("Error: FrameUtils::extractResources. Problem opening connection to bundleresource. Message: " + e.getMessage());
1991 } catch (NoSuchMethodException e) {
1992 System.err.println("Error: FrameUtils::extractResources. Unable to find method URLConnection::getFileURL. Message: " + e.getMessage());
1993 } catch (InvocationTargetException e) {
1994 System.err.println("Error: FrameUtils::extractResources. Problem invoking URLConnection::getFileURL. Message: " + e.getMessage());
1995 } catch (IllegalAccessException e) {
1996 System.err.println("Error: FrameUtils::extractResources. Problem invoking URLConnection::getFileURL. Message: " + e.getMessage());
1997 }
1998 } else {
1999 try {
2000 File folder = new File(resourceUrl.toURI().getPath());
2001 extractResourcesFromFolder(folder, source, destination);
2002 } catch (URISyntaxException e) {
2003 System.err.println("Error: FrameUtils::extractResources. Problem converting URL to URI. Message: " + e.getMessage());
2004 } catch (IOException e) {
2005 System.err.println("Error: FrameUtils::extractResources. Exception whilst extracting resources from folder. Message: " + e.getMessage());
2006 }
2007 }
2008
2009 // Create the .res file to signal completion
2010 try {
2011 destination.resolve(".res").toFile().createNewFile();
2012 } catch (IOException e) {
2013 System.err.println("Error: FrameUtils::extractResources. Unable to create the .res file to flag that resources have been extracted. Message: " + e.getMessage());
2014 }
2015
2016 return true;
2017 }
2018
2019 private static void extractFromJarFile(ClassLoader classLoader, Enumeration<JarEntry> jarEntries, String source, Path destination) throws IOException {
2020 while (jarEntries.hasMoreElements()) {
2021 ZipEntry ze = jarEntries.nextElement();
2022 if (!ze.getName().startsWith(source)) {
2023 continue;
2024 }
2025 File out = destination.resolve(ze.getName().substring(source.length())).toFile();
2026 if (ze.isDirectory()) {
2027 out.mkdirs();
2028 continue;
2029 }
2030 FileOutputStream fOut = null;
2031 InputStream fIn = null;
2032 try {
2033 fOut = new FileOutputStream(out);
2034 fIn = classLoader.getResourceAsStream(ze.getName());
2035 byte[] bBuffer = new byte[1024];
2036 int nLen;
2037 while ((nLen = fIn.read(bBuffer)) > 0) {
2038 fOut.write(bBuffer, 0, nLen);
2039 }
2040 fOut.flush();
2041 } catch (Exception e) {
2042 e.printStackTrace();
2043 } finally {
2044 if (fOut != null) {
2045 fOut.close();
2046 }
2047 if (fIn != null) {
2048 fIn.close();
2049 }
2050 }
2051 }
2052 }
2053
2054 private static void extractResourcesFromFolder(File folder, String source, Path destination) throws IOException {
2055 LinkedList<File> items = new LinkedList<File>();
2056 items.addAll(Arrays.asList(folder.listFiles()));
2057 LinkedList<File> files = new LinkedList<File>();
2058
2059 while (items.size() > 0) {
2060 File file = items.remove(0);
2061 if (file.isFile()) {
2062 if (!file.getName().contains(".svn")) {
2063 files.add(file);
2064 }
2065 } else {
2066 if (!file.getName().contains(".svn")) {
2067 items.addAll(Arrays.asList(file.listFiles()));
2068 }
2069 }
2070 }
2071 for (File file : files) {
2072 String path = file.getPath();
2073 System.out.println(path);
2074 Path relativize = folder.toPath().relativize(Paths.get(file.getPath()));
2075 File out = destination.resolve(relativize).toFile();
2076 copyFile(file, out, true);
2077 }
2078 }
2079
2080 /**
2081 * @param src
2082 * @param dst
2083 * @throws IOException
2084 */
2085 public static void copyFile(File src, File dst, boolean overWrite) throws IOException {
2086 if (!overWrite && dst.exists()) {
2087 return;
2088 }
2089
2090 dst.getParentFile().mkdirs();
2091 FileOutputStream fOut = null;
2092 FileInputStream fIn = null;
2093 try {
2094 // System.out.println(out.getPath());
2095 fOut = new FileOutputStream(dst);
2096 fIn = new FileInputStream(src);
2097 byte[] bBuffer = new byte[1024];
2098 int nLen;
2099 while ((nLen = fIn.read(bBuffer)) > 0) {
2100 fOut.write(bBuffer, 0, nLen);
2101 }
2102 fOut.flush();
2103 } catch (Exception e) {
2104 e.printStackTrace();
2105 } finally {
2106 if (fOut != null) {
2107 fOut.close();
2108 }
2109 if (fIn != null) {
2110 fIn.close();
2111 }
2112 }
2113 }
2114
2115 public static Text getLastEdited() {
2116 return LastEdited;
2117 }
2118
2119 public static Collection<Text> getCurrentTextItems() {
2120 Collection<Text> currentTextItems = new LinkedHashSet<Text>();
2121 Collection<Item> currentItems = getCurrentItems(null);
2122 if (currentItems != null) {
2123 for (Item i : getCurrentItems(null)) {
2124 if (i instanceof Text && !i.isLineEnd()) {
2125 currentTextItems.add((Text) i);
2126 }
2127 }
2128 }
2129 return currentTextItems;
2130 }
2131}
Note: See TracBrowser for help on using the repository browser.