source: trunk/src/org/expeditee/gui/FrameUtils.java@ 1424

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

When reconstructing the body on parse, make sure to use the source of XRayable Items

File size: 66.4 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.println("**** Have temporarily supressed MessageBay call, "
981 + "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
1166 if (item instanceof XRayable && !((XRayable) item).sourceIsAccessible()) {
1167 item = ((XRayable) item).getSource();
1168 }
1169
1170 String encryptionLabel = item.getEncryptionLabel();
1171 if (encryptionLabel == null || encryptionLabel.isEmpty()) {
1172 parsedBody.add(item);
1173 } else if (access.contains(encryptionLabel)) {
1174 parsedBody.add(item);
1175 } else {
1176 parsedBody.addAll(item.getSurrogates());
1177 }
1178 }
1179
1180 return parsedBody;
1181 }
1182
1183 /**
1184 * TODO: Comment. cts16
1185 *
1186 * @param vectors
1187 * @param permission
1188 * @param i
1189 */
1190 private static void addVector(List<Vector> vectors, UserAppliedPermission defaultPermission,
1191 UserAppliedPermission framePermission, Item i) {
1192 // TODO It is possible to get into an infinite loop if a
1193 // frame contains an @ao which leads to a frame with an
1194 // @v which points back to the frame with the @ao
1195 Frame vector = FrameIO.LoadFrame(i.getAbsoluteLink());
1196
1197 // Get the permission from off the vector frame
1198 UserAppliedPermission vectorPermission = UserAppliedPermission
1199 .getPermission(vector.getAnnotationValue("permission"), defaultPermission);
1200
1201 // If the frame permission is lower, use that
1202 vectorPermission = UserAppliedPermission.min(vectorPermission, framePermission);
1203
1204 // Highest permissable permission for vectors is copy
1205 vectorPermission = UserAppliedPermission.min(vectorPermission, UserAppliedPermission.copy);
1206 if (vector != null) {
1207 String scaleString = new AttributeValuePair(i.getText()).getValue();
1208 Float scale = 1F;
1209 try {
1210 scale = Float.parseFloat(scaleString);
1211 } catch (Exception e) {
1212 }
1213 Vector newVector = new Vector(vector, vectorPermission, scale, i);
1214 i.setOverlay(newVector);
1215 i.setVisible(false);
1216 vectors.add(newVector);
1217 }
1218 }
1219
1220 public static Item onItem(float floatX, float floatY, boolean changeLastEdited) {
1221 return onItem(DisplayController.getCurrentFrame(), floatX, floatY, changeLastEdited);
1222 }
1223
1224 /**
1225 * Searches through the list of items on this frame to find one at the given x,y
1226 * coordinates.
1227 *
1228 * @param x
1229 * The x coordinate
1230 * @param y
1231 * The y coordinate
1232 * @return The Item at the given coordinates, or NULL if none is found.
1233 */
1234 public static Item onItem(Frame toCheck, float floatX, float floatY, boolean bResetLastEdited) {
1235 // System.out.println("MouseX: " + floatX + " MouseY: " + floatY);
1236 int x = Math.round(floatX);
1237 int y = Math.round(floatY);
1238 if (toCheck == null) {
1239 return null;
1240 }
1241
1242 List<Item> possibles = new ArrayList<Item>(0);
1243
1244 // if the mouse is in the message area
1245 if (y >= DisplayController.getMessageBayPaintArea().getMinY()) {
1246 // check the individual bay items (MessageBay + MailBay)
1247 List<Item> bayItems = new LinkedList<Item>();
1248 if (DisplayController.isMailMode()) {
1249 bayItems.addAll(MailBay.getPreviewMessages());
1250 } else {
1251 bayItems.addAll(MessageBay.getMessages());
1252 }
1253 for (Item message : bayItems) {
1254 if (message != null) {
1255 if (message.contains(new Point(x, y))) {
1256 message.setOverlayPermission(UserAppliedPermission.copy);
1257 possibles.add(message);
1258 } else {
1259 // Not sure why but if the line below is removed then
1260 // several items can be highlighted at once
1261 message.setHighlightMode(Item.HighlightMode.None);
1262 message.setHighlightColorToDefault();
1263 }
1264 }
1265 }
1266
1267 // check the link to the message/mail frame
1268 Item linkItem = DisplayController.isMailMode() ? MailBay.getMailLink() : MessageBay.getMessageLink();
1269 if (linkItem != null && linkItem.contains(new Point(x, y))) {
1270 linkItem.setOverlayPermission(UserAppliedPermission.copy);
1271 possibles.add(linkItem);
1272 }
1273
1274 // this is taken into account in contains
1275 // y -= FrameGraphics.getMaxFrameSize().height;
1276 // otherwise, the mouse is on the frame
1277 } else {
1278 if (LastEdited != null) {
1279 if (LastEdited.contains(x, y) && !FreeItems.getInstance().contains(LastEdited)
1280 && LastEdited.getParent() == DisplayController.getCurrentFrame()
1281 && LastEdited.getParent().getSortedItems().contains(LastEdited)) {
1282 LastEdited.setOverlayPermission(UserAppliedPermission.full);
1283 return LastEdited;
1284 } else if (bResetLastEdited) {
1285 setLastEdited(null);
1286 }
1287 }
1288 ArrayList<Item> checkList = new ArrayList<Item>();
1289 checkList.addAll(toCheck.getInteractableItems());
1290 checkList.add(toCheck.getNameItem());
1291
1292 for (Item i : checkList) {
1293
1294 // do not check annotation items in audience mode
1295 // TODO: Upon hover of Rubbish Bin, Undo and Restore Widgets, flickering occurs
1296 // depending on the mouse distance from a corner. Resolve this.
1297 if (i.isVisible() && !(DisplayController.isAudienceMode() && i.isAnnotation())) {
1298 if (i instanceof WidgetCorner) {
1299 WidgetCorner wc = (WidgetCorner) i;
1300 if (wc.getWidgetSource() instanceof ButtonWidget) {
1301 ButtonWidget bw = (ButtonWidget) wc.getWidgetSource();
1302
1303 if (bw.getdropInteractableStatus() == true) {
1304 Widget iw = wc.getWidgetSource();
1305
1306 if (iw.getBounds().contains(x, y)) {
1307
1308 if (!FreeItems.getInstance().contains(i)) {
1309 possibles.add(i);
1310 }
1311 }
1312 }
1313 }
1314 }
1315
1316 if (i.contains(new Point(x, y))) {
1317 if (!FreeItems.getInstance().contains(i)) {
1318 possibles.add(i);
1319 }
1320 }
1321
1322 }
1323 }
1324 }
1325
1326 // if there are no possible items, return null
1327 if (possibles.size() == 0) {
1328 return null;
1329 }
1330
1331 // if there is only one possibility, return it
1332 if (possibles.size() == 1) {
1333 return possibles.get(0);
1334 }
1335
1336 // return closest x,y pair to mouse
1337 Item closest = possibles.get(0);
1338 int distance = (int) Math.round(
1339 Math.sqrt(Math.pow(Math.abs(closest.getX() - x), 2) + Math.pow(Math.abs(closest.getY() - y), 2)));
1340
1341 for (Item i : possibles) {
1342 int d = (int) Math
1343 .round(Math.sqrt(Math.pow(Math.abs(i.getX() - x), 2) + Math.pow(Math.abs(i.getY() - y), 2)));
1344
1345 // System.out.println(d);
1346 if (d <= distance) {
1347 distance = d;
1348
1349 // dots take precedence over lines
1350 if ((!(closest instanceof Dot && i instanceof Line))
1351 && (!(closest instanceof Text && i instanceof Line))) {
1352 closest = i;
1353 }
1354
1355 }
1356
1357 }
1358
1359 return closest;
1360 }
1361
1362 /**
1363 * Checks if the mouse is currently over an item.
1364 *
1365 * @return True if the mouse is over any item, false otherwise.
1366 */
1367 public static boolean hasCurrentItem() {
1368 return getCurrentItem() != null;
1369 }
1370
1371 public synchronized static Item getCurrentItem() {
1372 return onItem(DisplayController.getCurrentFrame(), DisplayController.getMouseX(), DisplayController.getMouseY(),
1373 true);
1374 }
1375
1376 public static PolygonBounds getEnlosingPolygon() {
1377 Collection<Item> enclosure = getEnclosingLineEnds();
1378
1379 if (enclosure == null || enclosure.size() == 0) {
1380 return null;
1381 }
1382
1383 return enclosure.iterator().next().getEnclosedShape();
1384 }
1385
1386 /**
1387 *
1388 * @param currentItem
1389 * @return
1390 */
1391 public static Collection<Item> getCurrentItems() {
1392 return getCurrentItems(getCurrentItem());
1393 }
1394
1395 public static Collection<Item> getCurrentItems(Item currentItem) {
1396 Collection<Item> enclosure = getEnclosingLineEnds();
1397
1398 if (enclosure == null || enclosure.size() == 0) {
1399 return null;
1400 }
1401
1402 Item firstItem = enclosure.iterator().next();
1403
1404 Collection<Item> enclosed = getItemsEnclosedBy(DisplayController.getCurrentFrame(),
1405 firstItem.getEnclosedShape());
1406
1407 // Brook: enclosed widgets are to be fully enclosed, never partially
1408 /*
1409 * MIKE says: but doesn't this mean that widgets are treated differently from
1410 * ALL other object which only need to be partially enclosed to be picked up
1411 */
1412 List<Widget> enclosedWidgets = new LinkedList<Widget>();
1413 for (Item i : enclosed) {
1414 // Don't want to lose the highlighting from the current item
1415 if (i == currentItem || enclosure.contains(i)) {
1416 continue;
1417 }
1418 // Don't want to lose the highlighting of connected Dots
1419 // TODO: this code does nothing (perhaps the continue is meant for the outer
1420 // for loop?). cts16
1421 if (i instanceof Dot && i.getHighlightMode() == HighlightMode.Connected) {
1422 for (Line l : i.getLines()) {
1423 if (l.getOppositeEnd(i).getHighlightMode() == HighlightMode.Normal) {
1424 continue;
1425 }
1426 }
1427 }
1428
1429 if (i instanceof WidgetCorner) {
1430 if (!enclosedWidgets.contains(((WidgetCorner) i).getWidgetSource())) {
1431 enclosedWidgets.add(((WidgetCorner) i).getWidgetSource());
1432 }
1433 }
1434
1435 i.setHighlightMode(Item.HighlightMode.None);
1436 i.setHighlightColorToDefault();
1437 }
1438
1439 for (Widget iw : enclosedWidgets) {
1440 for (Item i : iw.getItems()) {
1441 if (!enclosed.contains(i)) {
1442 enclosed.add(i);
1443 }
1444 }
1445 }
1446
1447 return enclosed;
1448 }
1449
1450 /**
1451 * Gets the collection of Dot items that form the enclosure nearest to the
1452 * current mouse position.
1453 */
1454 public static Collection<Item> getEnclosingLineEnds() {
1455 return getEnclosingLineEnds(new Point(DisplayController.getMouseX(), DisplayController.getMouseY()));
1456 }
1457
1458 /**
1459 * Gets the collection of Dot items that form the enclosure nearest to the given
1460 * position.
1461 */
1462 public static Collection<Item> getEnclosingLineEnds(Point position) {
1463 // update enclosed shapes
1464 Frame current = DisplayController.getCurrentFrame();
1465 if (current == null) {
1466 return null;
1467 }
1468 List<Item> items = current.getSortedItems();
1469
1470 // Remove all items that are connected to freeItems
1471 List<Item> freeItems = new ArrayList<Item>(FreeItems.getInstance());
1472 while (freeItems.size() > 0) {
1473 Item item = freeItems.get(0);
1474 Collection<Item> connected = item.getAllConnected();
1475 items.removeAll(connected);
1476 freeItems.removeAll(connected);
1477 }
1478
1479 List<Item> used = new ArrayList<Item>(0);
1480
1481 while (items.size() > 0) {
1482 Item i = items.get(0);
1483 items.remove(i);
1484 if (i.isEnclosed()) {
1485 PolygonBounds p = i.getEnclosedShape();
1486 if (p.contains(position)) {
1487 used.add(i);
1488 items.removeAll(i.getEnclosingDots());
1489 }
1490 }
1491 }
1492
1493 if (used.size() == 0) {
1494 return null;
1495 }
1496
1497 // if there is only one possibility, return it
1498 if (used.size() == 1) {
1499 return used.get(0).getEnclosingDots();
1500 // otherwise, determine which polygon is closest to the cursor
1501 } else {
1502 Collections.sort(used, new Comparator<Item>() {
1503 @Override
1504 public int compare(Item d1, Item d2) {
1505 PolygonBounds p1 = d1.getEnclosedShape();
1506 PolygonBounds p2 = d2.getEnclosedShape();
1507
1508 int closest = Integer.MAX_VALUE;
1509 int close2 = Integer.MAX_VALUE;
1510
1511 int mouseX = DisplayController.getMouseX();
1512 int mouseY = DisplayController.getMouseY();
1513
1514 for (int i = 0; i < p1.getPointCount(); i++) {
1515 int diff = Math.abs(p1.getPoint(i).getX() - mouseX) + Math.abs(p1.getPoint(i).getY() - mouseY);
1516 int diff2 = Integer.MAX_VALUE;
1517
1518 if (i < p2.getPointCount()) {
1519 diff2 = Math.abs(p2.getPoint(i).getX() - mouseX) + Math.abs(p2.getPoint(i).getY() - mouseY);
1520 }
1521
1522 if (diff < Math.abs(closest)) {
1523 close2 = closest;
1524 closest = diff;
1525 } else if (diff < Math.abs(close2)) {
1526 close2 = diff;
1527 }
1528
1529 if (diff2 < Math.abs(closest)) {
1530 close2 = closest;
1531 closest = -diff2;
1532 } else if (diff2 < Math.abs(close2)) {
1533 close2 = diff2;
1534 }
1535 }
1536
1537 if (closest > 0 && close2 > 0) {
1538 return -10;
1539 }
1540
1541 if (closest < 0 && close2 < 0) {
1542 return 10;
1543 }
1544
1545 if (closest > 0) {
1546 return -10;
1547 }
1548
1549 return 10;
1550 }
1551
1552 });
1553
1554 return used.get(0).getEnclosingDots();
1555 }
1556 }
1557
1558 // TODO Remove this method!!
1559 // Can just getItemsWithin be used?
1560 public static Collection<Item> getItemsEnclosedBy(Frame frame, PolygonBounds poly) {
1561 Collection<Item> contained = frame.getItemsWithin(poly);
1562
1563 Collection<Item> results = new LinkedHashSet<Item>(contained.size());
1564
1565 // check for correct permissions
1566 for (Item item : contained) {
1567 // if the item is on the frame
1568 if (item.getParent() == frame || item.getParent() == null) {
1569 // item.Permission = Permission.full;
1570 results.add(item);
1571 // otherwise, it must be on an overlay frame
1572 } else {
1573 for (Overlay overlay : frame.getOverlays()) {
1574 if (overlay.Frame == item.getParent()) {
1575 item.setOverlayPermission(overlay.permission);
1576 results.add(item);
1577 break;
1578 }
1579 }
1580 }
1581 }
1582
1583 return results;
1584 }
1585
1586 public static void CreateDefaultProfile(String profileFor, Frame profile) {
1587 CreateDefaultProfile(profileFor, profile, null, null);
1588 }
1589
1590 /**
1591 * Copies the content from the default profile to the specified profile.
1592 * @param profileFor Name of profile that is destination of copy.
1593 * @param profile Profile being setup.
1594 * @param specifiedTextSettings text settings to provide a default value for in the new profile
1595 * @param specifiedGenericSettings generic settings to provide a default value for in the new profile
1596 */
1597 public static void CreateDefaultProfile(String profileFor, Frame profile,
1598 Map<String, Setting> specifiedSettings, Map<String, Consumer<Frame>> notifyWhenGenerated) {
1599 // If this is already the default profile then nothing (other than setting
1600 // title) needs to be done.
1601 Text titleItem = profile.getTitleItem();
1602 Text title = titleItem;
1603 if (!profileFor.equals(UserSettings.DEFAULT_PROFILE_NAME)) {
1604 // If this profile is not the default profile, copy it from the default profile
1605 // instead of generating a new profile
1606 // (this allows the possibility of modifying the default profile and having any
1607 // new profiles get those modifications)
1608 Frame defaultFrame = FrameIO.LoadProfile(UserSettings.DEFAULT_PROFILE_NAME);
1609 if (defaultFrame == null) {
1610 try {
1611 // If we do not have a default to copy, create one.
1612 defaultFrame = FrameIO.CreateNewProfile(UserSettings.DEFAULT_PROFILE_NAME, null, null);
1613 } catch (InvalidFramesetNameException invalidNameEx) {
1614 MessageBay.errorMessage("Failed to create default profile named: "
1615 + UserSettings.DEFAULT_PROFILE_NAME + ". "
1616 + "Profile names must start and end with a letter and must contain only letters and numbers.");
1617 return;
1618 } catch (ExistingFramesetException existingFramesetEx) {
1619 MessageBay.errorMessage("Failed to create the desired default frameset: "
1620 + UserSettings.DEFAULT_PROFILE_NAME + ", "
1621 + "because it already exists. This should never happen as we shouldn't be asking to create it if it already exists.");
1622 return;
1623 }
1624 }
1625
1626 MessageBay.suppressMessages(true);
1627 int lastNumber = FrameIO.getLastNumber(defaultFrame.getFramesetName());
1628 for (int i = 1; i <= lastNumber; i++) {
1629 // Load in next default, if it doesn't exist continue loop.
1630 defaultFrame = FrameIO.LoadFrame(defaultFrame.getFramesetName() + i);
1631 if (defaultFrame == null) {
1632 continue;
1633 }
1634
1635 // Create the next next (currently blank) profile frame.
1636 // If there is frame gaps in the default (say if there is no 4.exp but there is
1637 // a 5.exp) then retain those gaps.
1638 // This way copied relative links work.
1639 while (profile.getNumber() < defaultFrame.getNumber()) {
1640 profile = FrameIO.CreateFrame(profile.getFramesetName(), null, null);
1641 }
1642 // Ensure we are working from a blank slate.
1643 profile.reset();
1644 profile.removeAllItems(profile.getAllItems());
1645
1646 // For each item on defaultFrame:
1647 // 1. Set all items to be relatively linked so once copied their links correctly
1648 // point to the frame on the created profile rather than the default profile.
1649 // 2. Copy item from defaultFrame to the current profile frame being
1650 // constructed.
1651 // 3. Replace settings values of copied items with those specified in
1652 // specifiedSettings (if present)
1653 for (Item item : defaultFrame.getAllItems()) {
1654 item.setRelativeLink();
1655 }
1656 profile.addAllItems(defaultFrame.getAllItems());
1657 if (i == 1 && titleItem != null) {
1658 titleItem.setText(profileFor + "'s Profile");
1659 }
1660 String category = profile.getTitle();
1661 List<String> settingsKeys = null;
1662 if (specifiedSettings != null) {
1663 settingsKeys = specifiedSettings.keySet().stream().filter(key ->
1664 key.startsWith(category)).collect(Collectors.toList());
1665 }
1666 if (settingsKeys != null) {
1667 for (String key: settingsKeys) {
1668 Setting setting = specifiedSettings.get(key);
1669 String name = setting.getName();
1670 Text representation = setting.generateRepresentation(name, profile.getFramesetName());
1671 Collection<Text> canditates = profile.getTextItems();
1672 canditates.removeIf(text -> !text.getText().startsWith(representation.getText().split(" ")[0]));
1673 canditates.forEach(text -> {
1674 Point backupPos = text.getPosition();
1675 Item.DuplicateItem(representation, text);
1676 text.setText(representation.getText());
1677 text.setPosition(backupPos);
1678 });
1679 }
1680 }
1681 if (notifyWhenGenerated != null && notifyWhenGenerated.containsKey(category)) {
1682 notifyWhenGenerated.get(category).accept(profile);
1683 }
1684 FrameIO.SaveFrame(profile);
1685 }
1686 MessageBay.suppressMessages(false);
1687 } else {
1688 title.setText("Default Profile Frame");
1689 int xPos = 300;
1690 int yPos = 100;
1691 Text t;
1692
1693 // Add documentation links
1694 File helpDirectory = new File(FrameIO.HELP_PATH);
1695 if (helpDirectory != null) {
1696 File[] helpFramesets = helpDirectory.listFiles();
1697 if (helpFramesets != null) {
1698
1699 // Add the title for the help index
1700 Text help = profile.addText(xPos, yPos, "@Expeditee Help", null);
1701 help.setSize(25);
1702 help.setFontStyle("Bold");
1703 help.setFamily("SansSerif");
1704 help.setColor(TemplateSettings.ColorWheel.get()[3]);
1705
1706 xPos += 25;
1707 System.out.println("Installing frameset: ");
1708
1709 boolean first_item = true;
1710 for (File helpFrameset : helpFramesets) {
1711 String framesetName = helpFrameset.getName();
1712 if (!FrameIO.isValidFramesetName(framesetName)) {
1713 continue;
1714 }
1715
1716 if (first_item) {
1717 System.out.print(" " + framesetName);
1718 first_item = false;
1719 } else {
1720 System.out.print(", " + framesetName);
1721 }
1722 System.out.flush();
1723
1724 Frame indexFrame = FrameIO.LoadFrame(framesetName + '1');
1725 // Look through the folder for help index pages
1726 if (indexFrame != null && ItemUtils.FindTag(indexFrame.getSortedItems(), "@HelpIndex") != null) {
1727 // yPos += spacing;
1728 yPos += 30;
1729 t = profile.addText(xPos, yPos, '@' + indexFrame.getFramesetName(), null);
1730 t.setLink(indexFrame.getName());
1731 t.setColor(Colour.GREY);
1732 }
1733 }
1734 System.out.println();
1735 }
1736 }
1737
1738 xPos = 50;
1739 yPos = 100;
1740
1741 // Populate Start Pages and Settings
1742 File framesetDirectory = new File(FrameIO.FRAME_PATH);
1743
1744 if (framesetDirectory.exists()) {
1745 File[] startpagesFramesets = framesetDirectory.listFiles();
1746
1747 if (startpagesFramesets != null) {
1748 // Add Start Page title
1749 Text templates = profile.addText(xPos, yPos, "@Start Pages", null);
1750 templates.setSize(25);
1751 templates.setFontStyle("Bold");
1752 templates.setFamily("SansSerif");
1753 templates.setColor(TemplateSettings.ColorWheel.get()[3]);
1754
1755 xPos += 25;
1756
1757 // Start Pages should be the first frame in its own frameset +
1758 // frameset name should be present in FrameUtils.startPages[].
1759 for (File startpagesFrameset : startpagesFramesets) {
1760 String framesetName = startpagesFrameset.getName();
1761
1762 // Only add link if frameset is a startpage
1763 for (int i = 0; i < startPages.length; i++) {
1764 if (framesetName.equals(startPages[i])) {
1765 Frame indexFrame = FrameIO.LoadFrame(framesetName + '1');
1766
1767 // Add start page link
1768 if (indexFrame != null) {
1769 yPos += 30;
1770 t = profile.addText(xPos, yPos, '@' + indexFrame.getFramesetName(), null);
1771 t.setLink(indexFrame.getName());
1772 t.setColor(Colour.GREY);
1773 }
1774 }
1775 }
1776 }
1777 }
1778 }
1779
1780 FrameIO.SaveFrame(profile);
1781
1782 // Populate settings frameset
1783 Settings.Init();
1784 t = profile.addText(550, 100, "@Settings", null);
1785 t.setSize((float) 25.0);
1786 t.setFamily("SansSerif");
1787 t.setFontStyle("Bold");
1788 t.setColor(Colour.GREY);
1789 Settings.generateSettingsTree(t);
1790 System.out.println("@Settings: Default settings generation complete.");
1791
1792 FrameIO.SaveFrame(profile);
1793 }
1794 }
1795
1796 private static void checkTDFCItemWaiting(Frame currentFrame) {
1797 Item tdfcItem = FrameUtils.getTdfcItem();
1798 // if there is a TDFC Item waiting
1799 if (tdfcItem != null) {
1800 boolean change = currentFrame.hasChanged();
1801 boolean saved = currentFrame.isSaved();
1802 // Save the parent of the item if it has not been saved
1803 if (!change && !saved) {
1804 tdfcItem.setLink(null);
1805 tdfcItem.getParent().setChanged(true);
1806 FrameIO.SaveFrame(tdfcItem.getParent());
1807 DisplayController.requestRefresh(true);
1808 } else {
1809 SessionStats.CreatedFrame();
1810 }
1811
1812 setTdfcItem(null);
1813 }
1814 }
1815
1816 public static void setTdfcItem(Item _tdfcItem) {
1817 FrameUtils._tdfcItem = _tdfcItem;
1818 }
1819
1820 public static Item getTdfcItem() {
1821 return FrameUtils._tdfcItem;
1822 }
1823
1824 public static void setLastEdited(Text lastEdited) {
1825 // If the lastEdited is being changed then check if its @i
1826 Frame toReparse = null;
1827 Frame toRecalculate = null;
1828 Frame toUpdateObservers = null;
1829
1830 if (LastEdited == null) {
1831 // System.out.print("N");
1832 } else if (LastEdited != null) {
1833 // System.out.print("T");
1834 Frame parent = LastEdited.getParentOrCurrentFrame();
1835
1836 if (lastEdited != LastEdited) {
1837 if (LastEdited.startsWith("@i")) {
1838 // Check if its an image that can be resized to fit a box
1839 // around it
1840 String text = LastEdited.getText();
1841 if (text.startsWith("@i:") && !Character.isDigit(text.charAt(text.length() - 1))) {
1842 Collection<Item> enclosure = FrameUtils.getEnclosingLineEnds(LastEdited.getPosition());
1843 if (enclosure != null) {
1844 for (Item i : enclosure) {
1845 if (i.isLineEnd() && i.isEnclosed()) {
1846 DisplayController.getCurrentFrame().removeAllItems(enclosure);
1847 AxisAlignedBoxBounds rect = i.getEnclosedBox();
1848 LastEdited.setText(LastEdited.getText() + " " + Math.round(rect.getWidth()));
1849 LastEdited.setPosition(rect.getTopLeft());
1850 LastEdited.setThickness(i.getThickness());
1851 LastEdited.setBorderColor(i.getColor());
1852 break;
1853 }
1854 }
1855 StandardGestureActions.deleteItems(enclosure, false);
1856 }
1857 }
1858 toReparse = parent;
1859 } else if (LastEdited.recalculateWhenChanged()) {
1860 toRecalculate = parent;
1861 }
1862
1863 if (parent.hasObservers()) {
1864 toUpdateObservers = parent;
1865 }
1866 // Update the formula if in XRay mode
1867 if (DisplayController.isXRayMode() && LastEdited.hasFormula()) {
1868 LastEdited.setFormula(LastEdited.getText());
1869 }
1870 }
1871 if (lastEdited != LastEdited && LastEdited.getText().length() == 0 && LastEdited.getMinWidth() == null) {
1872 parent.removeItem(LastEdited);
1873 }
1874 }
1875 LastEdited = lastEdited;
1876
1877 if (!DisplayController.isXRayMode()) {
1878 if (toReparse != null) {
1879 Parse(toReparse, false, false);
1880 } else {
1881 if (toRecalculate != null) {
1882 toRecalculate.recalculate();
1883 }
1884
1885 if (toUpdateObservers != null) {
1886 toUpdateObservers.notifyObservers(false);
1887 }
1888 }
1889 }
1890 }
1891
1892 /**
1893 * Extracts files/folders from assets/resources-public and assets/resources-private
1894 * to {Expeditee Home}/resources-public and {Expeditee Home}/resources-private respectively.
1895 * @param force if true, resources will be extracted even ifthey have already been extracted before.
1896 */
1897 public static void extractResources(boolean force) {
1898 // Ensure groups area exists
1899 if (UserSettings.PublicAndPrivateResources) {
1900 // Extract private resources
1901 Path resourcesPrivate = Paths.get(FrameIO.PARENT_FOLDER).resolve("resources-private");
1902 extractResources("org/expeditee/assets/resources-private", resourcesPrivate, force);
1903
1904 // Extract public resources
1905 Path resourcesPublic = Paths.get(FrameIO.PARENT_FOLDER).resolve("resources-public");
1906 extractResources("org/expeditee/assets/resources-public", resourcesPublic, force);
1907 } else if (AuthenticatorBrowser.isAuthenticationRequired()) {
1908 // Deal with the instance of being in the old regime but using authentication.
1909
1910 // Ensure additional framesets
1911 Path framesetsDir = Paths.get(FrameIO.FRAME_PATH);
1912 boolean extracted = extractResources("org/expeditee/assets/resources-public/framesets", framesetsDir, force);
1913
1914 // Ensure additional images
1915 Path imagesDir = Paths.get(FrameIO.IMAGES_PATH);
1916 extracted |= extractResources("org/expeditee/assets/resources-public/images", imagesDir, force);
1917
1918 // Ensure deaddrops area exists
1919 extracted |= Paths.get(FrameIO.PARENT_FOLDER).resolve("deaddrops").toFile().mkdir();
1920
1921 if (extracted) {
1922 @SuppressWarnings("resource")
1923 Scanner in = new Scanner(System.in);
1924 System.out.println("Extracting resources...In order to use authentication, you need a new default profile frameset.");
1925 System.out.println("This is a destructive process, your existing default profile frameset (if it exists) will be deleted.");
1926 System.out.print("Do you want to proceed? y/N:");
1927 System.out.flush();
1928 String answer = in.nextLine();
1929 if (!answer.toLowerCase().startsWith("y")) {
1930 System.out.println("Exiting...");
1931 System.exit(1);
1932 }
1933
1934 // Ensure the default profile is 'update to date'.
1935 //NB: this limits the potential for those running old regime with authentication the ability to customise the default profile.
1936 Path defaultProfile = Paths.get(FrameIO.PROFILE_PATH).resolve("default");
1937 try {
1938 Files.walkFileTree(defaultProfile, new FileVisitor<Path>() {
1939 @Override
1940 public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
1941 dir.toFile().delete();
1942 return FileVisitResult.CONTINUE;
1943 }
1944 @Override
1945 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
1946 return FileVisitResult.CONTINUE;
1947 }
1948 @Override
1949 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
1950 file.toFile().delete();
1951 return FileVisitResult.CONTINUE;
1952 }
1953 @Override
1954 public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
1955 return FileVisitResult.CONTINUE;
1956 }
1957 });
1958 } catch (IOException e) {
1959 e.printStackTrace();
1960 }
1961 }
1962 }
1963 }
1964
1965 private static boolean extractResources(String source, Path destination, boolean force) {
1966 // If resources have already been extracted, and we are not forcing the extraction, there is nothing to do.
1967 if (!force && destination.resolve(".res").toFile().exists()) {
1968 return false;
1969 }
1970
1971 System.out.println("Extracting/Installing resources to: " + destination.getFileName());
1972
1973 // Create the destination
1974 destination.getParent().toFile().mkdirs();
1975
1976 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
1977 URL resourceUrl = classLoader.getResource(source);
1978 if (resourceUrl.getProtocol().equals("jar")) {
1979 try {
1980 JarURLConnection ju_connection = (JarURLConnection) resourceUrl.openConnection();
1981 JarFile jf = ju_connection.getJarFile();
1982 Enumeration<JarEntry> jarEntries = jf.entries();
1983 extractFromJarFile(classLoader, jarEntries, source, destination);
1984 } catch (IOException e) {
1985 System.err.println("Error: FrameUtils::extractResources. Exception whilst extracting resources from Jar File. Message: " + e.getMessage());
1986 }
1987 } else if (resourceUrl.getProtocol().equals("bundleresource")) {
1988 try {
1989 URLConnection urlConnection = resourceUrl.openConnection();
1990 Class<?> c = urlConnection.getClass();
1991 java.lang.reflect.Method toInvoke = c.getMethod("getFileURL");
1992 URL fileURL = (URL) toInvoke.invoke(urlConnection);
1993 extractResourcesFromFolder(new File(fileURL.getPath()), source, destination);
1994 } catch (IOException e) {
1995 System.err.println("Error: FrameUtils::extractResources. Problem opening connection to bundleresource. Message: " + e.getMessage());
1996 } catch (NoSuchMethodException e) {
1997 System.err.println("Error: FrameUtils::extractResources. Unable to find method URLConnection::getFileURL. Message: " + e.getMessage());
1998 } catch (InvocationTargetException e) {
1999 System.err.println("Error: FrameUtils::extractResources. Problem invoking URLConnection::getFileURL. Message: " + e.getMessage());
2000 } catch (IllegalAccessException e) {
2001 System.err.println("Error: FrameUtils::extractResources. Problem invoking URLConnection::getFileURL. Message: " + e.getMessage());
2002 }
2003 } else {
2004 try {
2005 File folder = new File(resourceUrl.toURI().getPath());
2006 extractResourcesFromFolder(folder, source, destination);
2007 } catch (URISyntaxException e) {
2008 System.err.println("Error: FrameUtils::extractResources. Problem converting URL to URI. Message: " + e.getMessage());
2009 } catch (IOException e) {
2010 System.err.println("Error: FrameUtils::extractResources. Exception whilst extracting resources from folder. Message: " + e.getMessage());
2011 }
2012 }
2013
2014 // Create the .res file to signal completion
2015 try {
2016 destination.resolve(".res").toFile().createNewFile();
2017 } catch (IOException e) {
2018 System.err.println("Error: FrameUtils::extractResources. Unable to create the .res file to flag that resources have been extracted. Message: " + e.getMessage());
2019 }
2020
2021 return true;
2022 }
2023
2024 private static void extractFromJarFile(ClassLoader classLoader, Enumeration<JarEntry> jarEntries, String source, Path destination) throws IOException {
2025 while (jarEntries.hasMoreElements()) {
2026 ZipEntry ze = jarEntries.nextElement();
2027 if (!ze.getName().startsWith(source)) {
2028 continue;
2029 }
2030 File out = destination.resolve(ze.getName().substring(source.length())).toFile();
2031 if (ze.isDirectory()) {
2032 out.mkdirs();
2033 continue;
2034 }
2035 FileOutputStream fOut = null;
2036 InputStream fIn = null;
2037 try {
2038 fOut = new FileOutputStream(out);
2039 fIn = classLoader.getResourceAsStream(ze.getName());
2040 byte[] bBuffer = new byte[1024];
2041 int nLen;
2042 while ((nLen = fIn.read(bBuffer)) > 0) {
2043 fOut.write(bBuffer, 0, nLen);
2044 }
2045 fOut.flush();
2046 } catch (Exception e) {
2047 e.printStackTrace();
2048 } finally {
2049 if (fOut != null) {
2050 fOut.close();
2051 }
2052 if (fIn != null) {
2053 fIn.close();
2054 }
2055 }
2056 }
2057 }
2058
2059 private static void extractResourcesFromFolder(File folder, String source, Path destination) throws IOException {
2060 LinkedList<File> items = new LinkedList<File>();
2061 items.addAll(Arrays.asList(folder.listFiles()));
2062 LinkedList<File> files = new LinkedList<File>();
2063
2064 while (items.size() > 0) {
2065 File file = items.remove(0);
2066 if (file.isFile()) {
2067 if (!file.getName().contains(".svn")) {
2068 files.add(file);
2069 }
2070 } else {
2071 if (!file.getName().contains(".svn")) {
2072 items.addAll(Arrays.asList(file.listFiles()));
2073 }
2074 }
2075 }
2076 for (File file : files) {
2077 String path = file.getPath();
2078 System.out.println(path);
2079 Path relativize = folder.toPath().relativize(Paths.get(file.getPath()));
2080 File out = destination.resolve(relativize).toFile();
2081 copyFile(file, out, true);
2082 }
2083 }
2084
2085 /**
2086 * @param src
2087 * @param dst
2088 * @throws IOException
2089 */
2090 public static void copyFile(File src, File dst, boolean overWrite) throws IOException {
2091 if (!overWrite && dst.exists()) {
2092 return;
2093 }
2094
2095 dst.getParentFile().mkdirs();
2096 FileOutputStream fOut = null;
2097 FileInputStream fIn = null;
2098 try {
2099 // System.out.println(out.getPath());
2100 fOut = new FileOutputStream(dst);
2101 fIn = new FileInputStream(src);
2102 byte[] bBuffer = new byte[1024];
2103 int nLen;
2104 while ((nLen = fIn.read(bBuffer)) > 0) {
2105 fOut.write(bBuffer, 0, nLen);
2106 }
2107 fOut.flush();
2108 } catch (Exception e) {
2109 e.printStackTrace();
2110 } finally {
2111 if (fOut != null) {
2112 fOut.close();
2113 }
2114 if (fIn != null) {
2115 fIn.close();
2116 }
2117 }
2118 }
2119
2120 public static Text getLastEdited() {
2121 return LastEdited;
2122 }
2123
2124 public static Collection<Text> getCurrentTextItems() {
2125 Collection<Text> currentTextItems = new LinkedHashSet<Text>();
2126 Collection<Item> currentItems = getCurrentItems(null);
2127 if (currentItems != null) {
2128 for (Item i : getCurrentItems(null)) {
2129 if (i instanceof Text && !i.isLineEnd()) {
2130 currentTextItems.add((Text) i);
2131 }
2132 }
2133 }
2134 return currentTextItems;
2135 }
2136}
Note: See TracBrowser for help on using the repository browser.