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

Last change on this file since 1421 was 1421, checked in by bln4, 5 years ago
File size: 66.5 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 // transfer any encryption label that is present
650 String encryptionLabel = txt.getEncryptionLabel();
651 if (encryptionLabel != null && !encryptionLabel.isEmpty()) {
652 pic.setEncryptionLabelOnLoad(encryptionLabel);
653 }
654
655 // if the picture could not be created successfully
656 if (pic == null) {
657 String imagePath = txt.getText();
658 assert (imagePath != null);
659 imagePath = new AttributeValuePair(imagePath).getValue().trim();
660 if (imagePath.length() == 0) {
661 return false;
662 // MessageBay.errorMessage("Expected image path after @i:");
663 } else {
664 MessageBay.errorMessage("Image " + imagePath + " could not be loaded");
665 }
666 return false;
667 }
668 frame.addItem(pic, true, items);
669
670 return true;
671 }
672
673 private static boolean createPictureInBody(Frame frame, Text txt) {
674 return createPicture(frame, txt, frame.getBody());
675 }
676
677 /**
678 * Creates an interactive widget and adds it to a frame. If txt has no parent
679 * the parent will be set to frame.
680 *
681 * @param frame
682 * Frame to add widget to. Must not be null.
683 *
684 * @param txt
685 * Text to create the widget from. Must not be null.
686 *
687 * @return True if created/added. False if could not create.
688 *
689 * @author Brook Novak
690 */
691 private static boolean createWidget(Frame frame, Text txt, ItemsList list) {
692
693 if (frame == null) {
694 throw new NullPointerException("frame");
695 }
696 if (txt == null) {
697 throw new NullPointerException("txt");
698 }
699
700 // Safety
701 if (txt.getParent() == null) {
702 txt.setParent(frame);
703 }
704
705 Widget iw = null;
706
707 try {
708
709 iw = Widget.createWidget(txt);
710
711 } catch (InteractiveWidgetNotAvailableException e) {
712 e.printStackTrace();
713 MessageBay.errorMessage("Cannot create iWidget: " + e.getMessage());
714 } catch (InteractiveWidgetInitialisationFailedException e) {
715 e.printStackTrace();
716 MessageBay.errorMessage("Cannot create iWidget: " + e.getMessage());
717 } catch (IllegalArgumentException e) {
718 e.printStackTrace();
719 MessageBay.errorMessage("Cannot create iWidget: " + e.getMessage());
720 }
721
722 if (iw == null) {
723 return false;
724 }
725
726 frame.removeItem(txt, true, list);
727
728 frame.addAllItems(iw.getItems(), list);
729
730 return true;
731 }
732
733 private static boolean createWidgetInBody(Frame frame, Text txt) {
734 return createWidget(frame, txt, frame.getBody());
735 }
736
737 public static List<String> ParseProfile(Frame profile) {
738 List<String> errors = new LinkedList<String>();
739
740 if (profile == null) {
741 return errors;
742 }
743
744 /*
745 * Make sure the correct cursor shows when turning off the custom cursor and
746 * reparsing the profile frame
747 */
748 FreeItems.getCursor().clear();
749 DisplayController.setCursor(Item.HIDDEN_CURSOR);
750 DisplayController.setCursor(Item.DEFAULT_CURSOR);
751
752 // check for settings tags
753 for (Text item : profile.getBodyTextItems(true)) {
754 try {
755
756 AttributeValuePair avp = new AttributeValuePair(item.getText());
757 String attributeFullCase = avp.getAttributeOrValue();
758
759 if (attributeFullCase == null) {
760 continue;
761 }
762
763 String attribute = attributeFullCase.trim().toLowerCase().replaceAll("^@", "");
764
765 if (attribute.equals("settings")) {
766 Settings.parseSettings(item);
767 }
768
769 } catch (Exception e) {
770 if (e.getMessage() != null) {
771 errors.add(e.getMessage());
772 } else {
773 e.printStackTrace();
774 errors.add("Error parsing [" + item.getText() + "] on " + profile.getName());
775 }
776 }
777 }
778
779 return errors;
780 }
781
782 /**
783 * Sets the first frame to be displayed.
784 *
785 * @param profile
786 */
787 public static void loadFirstFrame(Frame profile) {
788 if (UserSettings.HomeFrame.get() == null) {
789 UserSettings.HomeFrame.set(profile.getName());
790 }
791
792 Frame firstFrame = FrameIO.LoadFrame(UserSettings.HomeFrame.get());
793 if (firstFrame == null) {
794 MessageBay.warningMessage("Home frame not found: " + UserSettings.HomeFrame);
795 UserSettings.HomeFrame.set(profile.getName());
796 DisplayController.setCurrentFrame(profile, true);
797 } else {
798 DisplayController.setCurrentFrame(firstFrame, true);
799 }
800
801 }
802
803 public static Colour[] getColorWheel(Frame frame) {
804 if (frame != null) {
805 List<Text> textItems = frame.getBodyTextItems(false);
806 Colour[] colorList = new Colour[textItems.size() + 1];
807 for (int i = 0; i < textItems.size(); i++) {
808 colorList[i] = textItems.get(i).getColor();
809 }
810 // Make the last item transparency or default for forecolor
811 colorList[colorList.length - 1] = null;
812
813 return colorList;
814 }
815 return new Colour[] { Colour.BLACK, Colour.WHITE, null };
816 }
817
818 public static String getLink(Item item, String alt) {
819 if (item == null || !(item instanceof Text)) {
820 return alt;
821 }
822
823 AttributeValuePair avp = new AttributeValuePair(item.getText());
824 assert (avp != null);
825
826 if (avp.hasPair() && avp.getValue().trim().length() != 0) {
827 item.setLink(avp.getValue());
828 return avp.getValue();
829 } else if (item.getLink() != null) {
830 return item.getAbsoluteLink();
831 }
832
833 return alt;
834 }
835
836 public static String getDir(String name) {
837 if (name != null) {
838 File tester = new File(name);
839 if (tester.exists() && tester.isDirectory()) {
840 if (name.endsWith(File.separator)) {
841 return name;
842 } else {
843 return name + File.separator;
844 }
845 } else {
846 throw new RuntimeException("Directory not found: " + name);
847 }
848 }
849 throw new RuntimeException("Missing value for profile attribute" + name);
850 }
851
852 public static ArrayList<String> getDirs(Item item) {
853 ArrayList<String> dirsToAdd = new ArrayList<String>();
854 String dirListFrameName = item.getAbsoluteLink();
855 if (dirListFrameName != null) {
856 Frame dirListFrame = FrameIO.LoadFrame(dirListFrameName);
857 if (dirListFrame != null) {
858 for (Text t : dirListFrame.getBodyTextItems(false)) {
859 String dirName = t.getText().trim();
860 File tester = new File(dirName);
861 if (tester.exists() && tester.isDirectory()) {
862 if (dirName.endsWith(File.separator)) {
863 dirsToAdd.add(dirName);
864 } else {
865 dirsToAdd.add(dirName + File.separator);
866 }
867 }
868 }
869 }
870 }
871
872 return dirsToAdd;
873 }
874
875 private static void transformOutOfPlaceItems(Frame toParse, ItemsList toTransform) {
876 // Get all items from toTransform that have not been marked as deleted.
877 List<Item> items = toParse.getItems(false, toTransform);
878
879 // if XRayMode is on, replace pictures with their underlying text
880 if (DisplayController.isXRayMode()) {
881
882 // BROOK: Must handle these a little different
883 List<Widget> widgets = toParse.getInteractiveWidgets();
884
885 for (Item i : items) {
886 if (i instanceof XRayable) {
887 toParse.removeItem(i, true, toTransform);
888 // Show the items
889 for (Item item : ((XRayable) i).getConnected()) {
890 item.setVisible(true);
891 item.removeEnclosure(i);
892 }
893 } else if (i instanceof WidgetCorner) {
894 toParse.removeItem(i, true, toTransform);
895 } else if (i instanceof WidgetEdge) {
896 toParse.removeItem(i, true, toTransform);
897 } else if (i.hasFormula()) {
898 i.setText(i.getFormula());
899 } else if (i.hasOverlay()) {
900 i.setVisible(true);
901 // int x = i.getBoundsHeight();
902 }
903 }
904
905 for (Widget iw : widgets) {
906 toParse.addItem(iw.getSource(), true, toTransform);
907 }
908 }
909
910
911 // disable reading of cached overlays if in twinframes mode
912 if (DisplayController.isTwinFramesOn()) {
913 FrameIO.SuspendCache();
914 }
915
916 toParse.clearAnnotations();
917
918 // check for any new overlay items
919 items = toParse.getItems(false, toTransform);
920 for (Item i : items) {
921 try {
922 if (i instanceof WidgetCorner) {
923 // TODO improve efficiency so it only updates once... using
924 // observer design pattern
925 i.update();
926 } else if (i instanceof Text) {
927 if (!DisplayController.isXRayMode() && i.isAnnotation()) {
928 if (ItemUtils.startsWithTag(i, ItemUtils.TAG_IMAGE, true)) {
929 if (!i.hasEnclosures()) {
930 createPicture(toParse, (Text) i, toTransform);
931 }
932 // check for frame images
933 } else if (ItemUtils.startsWithTag(i, ItemUtils.TAG_FRAME_IMAGE) && i.getLink() != null
934 && !i.getAbsoluteLink().equalsIgnoreCase(toParse.getName())) {
935 XRayable image = null;
936 if (i.hasEnclosures()) {
937 // i.setHidden(true);
938 // image =
939 // i.getEnclosures().iterator().next();
940 // image.refresh();
941 } else {
942 image = new FrameImage((Text) i, null);
943 }
944 // TODO Add the image when creating new
945 // FrameImage
946 toParse.addItem(image, true, toTransform);
947 } else if (ItemUtils.startsWithTag(i, ItemUtils.TAG_BITMAP_IMAGE) && i.getLink() != null
948 && !i.getAbsoluteLink().equalsIgnoreCase(toParse.getName())) {
949 XRayable image = null;
950 if (i.hasEnclosures()) {
951 // image =
952 // i.getEnclosures().iterator().next();
953 // image.refresh();
954 // i.setHidden(true);
955 } else {
956 // If a new bitmap is created for a
957 // frame which already has a bitmap dont
958 // recreate the bitmap
959 image = new FrameBitmap((Text) i, null);
960 }
961 toParse.addItem(image, true, toTransform);
962 } else if (ItemUtils.startsWithTag(i, "@c")) {
963 // Can only have a @c
964 if (!i.hasEnclosures() && i.getLines().size() == 1) {
965 Circle circle = new Circle((Text) i);
966 toParse.addItem(circle, true, toTransform);
967 }
968 // Check for JSItem
969 } else if (ItemUtils.startsWithTag(i, "@js")) {
970 JSItem jsItem = new JSItem((Text) i);
971 toParse.addItem(jsItem, true, toTransform);
972 // Check for interactive widgets
973 } else if (ItemUtils.startsWithTag(i, ItemUtils.TAG_IWIDGET)) {
974 createWidget(toParse, (Text) i, toTransform);
975 }
976
977 // TODO decide exactly what to do here!!
978 toParse.addAnnotation((Text) i);
979 } else if (!DisplayController.isXRayMode() && i.hasFormula()) {
980 i.calculate(i.getFormula());
981 }
982 }
983 } catch (Exception e) {
984 Logger.Log(e);
985 e.printStackTrace();
986 System.err
987 .println("**** Have temporarily supressed MessageBay call, as resulted in infinite recursion");
988 // MessageBay.warningMessage("Exception occured when loading " +
989 // i.getClass().getSimpleName() + "(ID: "
990 // + i.getID() + ") " + e.getMessage() != null ? e.getMessage() : "");
991 }
992 }
993
994 /*
995 * for (Item i : items) { if (i instanceof Dot) { ((Dot)
996 * i).setPointType(pointtype); ((Dot) i).useFilledPoints(filledPoints); } }
997 */
998
999 if (DisplayController.isTwinFramesOn()) {
1000 FrameIO.ResumeCache();
1001 }
1002
1003
1004 }
1005
1006 private static void generatingSupportingItems(Frame toParse,
1007 ItemsList toBuildOff, boolean ignoreAnnotations) {
1008 // Get all items from toBuildOff that have not been marked as deleted.
1009 List<Item> items = toParse.getItems(false, toBuildOff);
1010
1011 List<Overlay> overlays = new ArrayList<Overlay>();
1012 List<Vector> vectors = new ArrayList<Vector>();
1013
1014 // disable reading of cached overlays if in twinframes mode
1015 if (DisplayController.isTwinFramesOn()) {
1016 FrameIO.SuspendCache();
1017 }
1018
1019 UserAppliedPermission permission = toParse.getUserAppliedPermission();
1020 // check for any new overlay items
1021 for (Item i : items) {
1022 try {
1023 // reset overlay permission
1024 i.setOverlayPermission(null);
1025 if (i instanceof Text) {
1026 if (i.isAnnotation()) {
1027 if (!DisplayController.isXRayMode() && ItemUtils.startsWithTag(i, ItemUtils.TAG_VECTOR)
1028 && i.getLink() != null) {
1029 if (!i.getAbsoluteLink().equals(toParse.getName())) {
1030 addVector(vectors, UserAppliedPermission.none, permission, i);
1031 }
1032 } else if (!DisplayController.isXRayMode()
1033 && ItemUtils.startsWithTag(i, ItemUtils.TAG_ACTIVE_VECTOR) && i.getLink() != null) {
1034 if (!i.getAbsoluteLink().equals(toParse.getName())) {
1035 addVector(vectors, UserAppliedPermission.followLinks, permission, i);
1036 }
1037 }
1038 // check for new OVERLAY items
1039 else if (!ignoreAnnotations && ItemUtils.startsWithTag(i, ItemUtils.TAG_OVERLAY)
1040 && i.getLink() != null) {
1041 if (i.getAbsoluteLink().equalsIgnoreCase(toParse.getName())) {
1042 // This frame contains an active overlay which
1043 // points to itself
1044 MessageBay.errorMessage(toParse.getName() + " contains an @o which links to itself");
1045 continue;
1046 }
1047
1048 Frame overlayFrame = FrameIO.LoadFrame(i.getAbsoluteLink());
1049 // Parse(overlay);
1050 if (overlayFrame != null && Overlay.getOverlay(overlays, overlayFrame) == null) {
1051 overlays.add(new Overlay(overlayFrame, UserAppliedPermission.none));
1052 }
1053 }
1054 // check for ACTIVE_OVERLAY items
1055 else if (!ignoreAnnotations && ItemUtils.startsWithTag(i, ItemUtils.TAG_ACTIVE_OVERLAY)
1056 && i.getLink() != null) {
1057 String link = i.getAbsoluteLink();
1058 if (link.equalsIgnoreCase(toParse.getName())) {
1059 // This frame contains an active overlay which
1060 // points to itself
1061 MessageBay.errorMessage(toParse.getName() + " contains an @ao which links to itself");
1062 continue;
1063 }
1064 Frame overlayFrame = null;
1065
1066 Frame current = DisplayController.getCurrentFrame();
1067 if (current != null) {
1068 for (Overlay o : current.getOverlays()) {
1069 if (o.Frame.getName().equalsIgnoreCase(link)) {
1070 overlayFrame = o.Frame;
1071 }
1072 }
1073 }
1074 if (overlayFrame == null) {
1075 overlayFrame = FrameIO.LoadFrame(link);
1076 }
1077
1078 // get level if specified
1079 String level = new AttributeValuePair(i.getText()).getValue();
1080 // default permission (if none is specified)
1081 PermissionTriple permissionLevel = new PermissionTriple(level,
1082 UserAppliedPermission.followLinks);
1083
1084 if (overlayFrame != null) {
1085 Overlay existingOverlay = Overlay.getOverlay(overlays, overlayFrame);
1086 // If it wasn't in the list create it and add
1087 // it.
1088 if (existingOverlay == null) {
1089 Overlay newOverlay = new Overlay(overlayFrame,
1090 permissionLevel.getPermission(overlayFrame.getOwner(), overlayFrame.getGroupMembers()));
1091 i.setOverlay(newOverlay);
1092 overlays.add(newOverlay);
1093 } else {
1094 existingOverlay.Frame.setPermission(permissionLevel);
1095 }
1096 }
1097 }
1098
1099 }
1100 }
1101 } catch (Exception e) {
1102 Logger.Log(e);
1103 e.printStackTrace();
1104 System.err.println("**** Have temporarily supressed MessageBay call, as resulted in infinite recursion");
1105 //MessageBay.warningMessage("Exception occured when loading " + i.getClass().getSimpleName() + "(ID: "
1106 // + i.getID() + ") " + e.getMessage() != null ? e.getMessage() : "");
1107 }
1108 }
1109
1110 /*
1111 * for (Item i : items) { if (i instanceof Dot) { ((Dot)
1112 * i).setPointType(pointtype); ((Dot) i).useFilledPoints(filledPoints); } }
1113 */
1114
1115 if (DisplayController.isTwinFramesOn()) {
1116 FrameIO.ResumeCache();
1117 }
1118
1119 toParse.clearOverlays();
1120 toParse.clearVectors();
1121 toParse.addAllOverlays(overlays);
1122 toParse.addAllVectors(vectors);
1123 }
1124
1125 public static void Parse(Frame toParse) {
1126 Parse(toParse, false);
1127 }
1128
1129 /**
1130 * Checks for any special Annotation items and updates the display as necessary.
1131 * Special Items: Images, overlays, sort.
1132 *
1133 */
1134 public static void Parse(Frame toParse, boolean firstParse) {
1135 Parse(toParse, firstParse, false);
1136 }
1137
1138 /**
1139 *
1140 * @param toParse
1141 * @param firstParse
1142 * @param ignoreAnnotations
1143 * used to prevent infinate loops such as when performing TDFC with
1144 * an ao tag linked to a frame with an frameImage of a frame which
1145 * also has an ao tag on it.
1146 */
1147 public static void Parse(Frame toParse, boolean firstParse, boolean ignoreAnnotations) {
1148 List<String> accessList = Label.getAccessibleLabelsNames(new ItemsList(toParse.getPrimaryBody()));
1149
1150 ItemsList primaries = toParse.getPrimaryBody();
1151 ItemsList surrogates = toParse.getSurrogateBody();
1152
1153 transformOutOfPlaceItems(toParse, primaries);
1154 transformOutOfPlaceItems(toParse, surrogates);
1155
1156 toParse.getInteractableItems().clear();
1157 List<Item> newBody = parseFromPrimary(primaries, accessList);
1158 toParse.getBody().clear();
1159 toParse.getBody().addAll(newBody);
1160 generatingSupportingItems(toParse, toParse.getBody(), ignoreAnnotations);
1161
1162 if (firstParse) {
1163 ItemUtils.EnclosedCheck(toParse.getSortedItems());
1164 }
1165 }
1166
1167 private static List<Item> parseFromPrimary(ItemsList primaryBody, List<String> access) {
1168 List<Item> parsedBody = new ArrayList<Item>();
1169
1170 for (Item item: primaryBody) {
1171 String encryptionLabel = item.getEncryptionLabel();
1172 if (encryptionLabel == null || encryptionLabel.isEmpty()) {
1173 parsedBody.add(item);
1174 } else if (access.contains(encryptionLabel)) {
1175 parsedBody.add(item);
1176 } else {
1177 parsedBody.addAll(item.getSurrogates());
1178 }
1179 }
1180
1181 return parsedBody;
1182 }
1183
1184 /**
1185 * TODO: Comment. cts16
1186 *
1187 * @param vectors
1188 * @param permission
1189 * @param i
1190 */
1191 private static void addVector(List<Vector> vectors, UserAppliedPermission defaultPermission,
1192 UserAppliedPermission framePermission, Item i) {
1193 // TODO It is possible to get into an infinite loop if a
1194 // frame contains an @ao which leads to a frame with an
1195 // @v which points back to the frame with the @ao
1196 Frame vector = FrameIO.LoadFrame(i.getAbsoluteLink());
1197
1198 // Get the permission from off the vector frame
1199 UserAppliedPermission vectorPermission = UserAppliedPermission
1200 .getPermission(vector.getAnnotationValue("permission"), defaultPermission);
1201
1202 // If the frame permission is lower, use that
1203 vectorPermission = UserAppliedPermission.min(vectorPermission, framePermission);
1204
1205 // Highest permissable permission for vectors is copy
1206 vectorPermission = UserAppliedPermission.min(vectorPermission, UserAppliedPermission.copy);
1207 if (vector != null) {
1208 String scaleString = new AttributeValuePair(i.getText()).getValue();
1209 Float scale = 1F;
1210 try {
1211 scale = Float.parseFloat(scaleString);
1212 } catch (Exception e) {
1213 }
1214 Vector newVector = new Vector(vector, vectorPermission, scale, i);
1215 i.setOverlay(newVector);
1216 i.setVisible(false);
1217 vectors.add(newVector);
1218 }
1219 }
1220
1221 public static Item onItem(float floatX, float floatY, boolean changeLastEdited) {
1222 return onItem(DisplayController.getCurrentFrame(), floatX, floatY, changeLastEdited);
1223 }
1224
1225 /**
1226 * Searches through the list of items on this frame to find one at the given x,y
1227 * coordinates.
1228 *
1229 * @param x
1230 * The x coordinate
1231 * @param y
1232 * The y coordinate
1233 * @return The Item at the given coordinates, or NULL if none is found.
1234 */
1235 public static Item onItem(Frame toCheck, float floatX, float floatY, boolean bResetLastEdited) {
1236 // System.out.println("MouseX: " + floatX + " MouseY: " + floatY);
1237 int x = Math.round(floatX);
1238 int y = Math.round(floatY);
1239 if (toCheck == null) {
1240 return null;
1241 }
1242
1243 List<Item> possibles = new ArrayList<Item>(0);
1244
1245 // if the mouse is in the message area
1246 if (y >= DisplayController.getMessageBayPaintArea().getMinY()) {
1247 // check the individual bay items (MessageBay + MailBay)
1248 List<Item> bayItems = new LinkedList<Item>();
1249 if (DisplayController.isMailMode()) {
1250 bayItems.addAll(MailBay.getPreviewMessages());
1251 } else {
1252 bayItems.addAll(MessageBay.getMessages());
1253 }
1254 for (Item message : bayItems) {
1255 if (message != null) {
1256 if (message.contains(new Point(x, y))) {
1257 message.setOverlayPermission(UserAppliedPermission.copy);
1258 possibles.add(message);
1259 } else {
1260 // Not sure why but if the line below is removed then
1261 // several items can be highlighted at once
1262 message.setHighlightMode(Item.HighlightMode.None);
1263 message.setHighlightColorToDefault();
1264 }
1265 }
1266 }
1267
1268 // check the link to the message/mail frame
1269 Item linkItem = DisplayController.isMailMode() ? MailBay.getMailLink() : MessageBay.getMessageLink();
1270 if (linkItem != null && linkItem.contains(new Point(x, y))) {
1271 linkItem.setOverlayPermission(UserAppliedPermission.copy);
1272 possibles.add(linkItem);
1273 }
1274
1275 // this is taken into account in contains
1276 // y -= FrameGraphics.getMaxFrameSize().height;
1277 // otherwise, the mouse is on the frame
1278 } else {
1279 if (LastEdited != null) {
1280 if (LastEdited.contains(x, y) && !FreeItems.getInstance().contains(LastEdited)
1281 && LastEdited.getParent() == DisplayController.getCurrentFrame()
1282 && LastEdited.getParent().getSortedItems().contains(LastEdited)) {
1283 LastEdited.setOverlayPermission(UserAppliedPermission.full);
1284 return LastEdited;
1285 } else if (bResetLastEdited) {
1286 setLastEdited(null);
1287 }
1288 }
1289 ArrayList<Item> checkList = new ArrayList<Item>();
1290 checkList.addAll(toCheck.getInteractableItems());
1291 checkList.add(toCheck.getNameItem());
1292
1293 for (Item i : checkList) {
1294
1295 // do not check annotation items in audience mode
1296 // TODO: Upon hover of Rubbish Bin, Undo and Restore Widgets, flickering occurs
1297 // depending on the mouse distance from a corner. Resolve this.
1298 if (i.isVisible() && !(DisplayController.isAudienceMode() && i.isAnnotation())) {
1299 if (i instanceof WidgetCorner) {
1300 WidgetCorner wc = (WidgetCorner) i;
1301 if (wc.getWidgetSource() instanceof ButtonWidget) {
1302 ButtonWidget bw = (ButtonWidget) wc.getWidgetSource();
1303
1304 if (bw.getdropInteractableStatus() == true) {
1305 Widget iw = wc.getWidgetSource();
1306
1307 if (iw.getBounds().contains(x, y)) {
1308
1309 if (!FreeItems.getInstance().contains(i)) {
1310 possibles.add(i);
1311 }
1312 }
1313 }
1314 }
1315 }
1316
1317 if (i.contains(new Point(x, y))) {
1318 if (!FreeItems.getInstance().contains(i)) {
1319 possibles.add(i);
1320 }
1321 }
1322
1323 }
1324 }
1325 }
1326
1327 // if there are no possible items, return null
1328 if (possibles.size() == 0) {
1329 return null;
1330 }
1331
1332 // if there is only one possibility, return it
1333 if (possibles.size() == 1) {
1334 return possibles.get(0);
1335 }
1336
1337 // return closest x,y pair to mouse
1338 Item closest = possibles.get(0);
1339 int distance = (int) Math.round(
1340 Math.sqrt(Math.pow(Math.abs(closest.getX() - x), 2) + Math.pow(Math.abs(closest.getY() - y), 2)));
1341
1342 for (Item i : possibles) {
1343 int d = (int) Math
1344 .round(Math.sqrt(Math.pow(Math.abs(i.getX() - x), 2) + Math.pow(Math.abs(i.getY() - y), 2)));
1345
1346 // System.out.println(d);
1347 if (d <= distance) {
1348 distance = d;
1349
1350 // dots take precedence over lines
1351 if ((!(closest instanceof Dot && i instanceof Line))
1352 && (!(closest instanceof Text && i instanceof Line))) {
1353 closest = i;
1354 }
1355
1356 }
1357
1358 }
1359
1360 return closest;
1361 }
1362
1363 /**
1364 * Checks if the mouse is currently over an item.
1365 *
1366 * @return True if the mouse is over any item, false otherwise.
1367 */
1368 public static boolean hasCurrentItem() {
1369 return getCurrentItem() != null;
1370 }
1371
1372 public synchronized static Item getCurrentItem() {
1373 return onItem(DisplayController.getCurrentFrame(), DisplayController.getMouseX(), DisplayController.getMouseY(),
1374 true);
1375 }
1376
1377 public static PolygonBounds getEnlosingPolygon() {
1378 Collection<Item> enclosure = getEnclosingLineEnds();
1379
1380 if (enclosure == null || enclosure.size() == 0) {
1381 return null;
1382 }
1383
1384 return enclosure.iterator().next().getEnclosedShape();
1385 }
1386
1387 /**
1388 *
1389 * @param currentItem
1390 * @return
1391 */
1392 public static Collection<Item> getCurrentItems() {
1393 return getCurrentItems(getCurrentItem());
1394 }
1395
1396 public static Collection<Item> getCurrentItems(Item currentItem) {
1397 Collection<Item> enclosure = getEnclosingLineEnds();
1398
1399 if (enclosure == null || enclosure.size() == 0) {
1400 return null;
1401 }
1402
1403 Item firstItem = enclosure.iterator().next();
1404
1405 Collection<Item> enclosed = getItemsEnclosedBy(DisplayController.getCurrentFrame(),
1406 firstItem.getEnclosedShape());
1407
1408 // Brook: enclosed widgets are to be fully enclosed, never partially
1409 /*
1410 * MIKE says: but doesn't this mean that widgets are treated differently from
1411 * ALL other object which only need to be partially enclosed to be picked up
1412 */
1413 List<Widget> enclosedWidgets = new LinkedList<Widget>();
1414 for (Item i : enclosed) {
1415 // Don't want to lose the highlighting from the current item
1416 if (i == currentItem || enclosure.contains(i)) {
1417 continue;
1418 }
1419 // Don't want to lose the highlighting of connected Dots
1420 // TODO: this code does nothing (perhaps the continue is meant for the outer
1421 // for loop?). cts16
1422 if (i instanceof Dot && i.getHighlightMode() == HighlightMode.Connected) {
1423 for (Line l : i.getLines()) {
1424 if (l.getOppositeEnd(i).getHighlightMode() == HighlightMode.Normal) {
1425 continue;
1426 }
1427 }
1428 }
1429
1430 if (i instanceof WidgetCorner) {
1431 if (!enclosedWidgets.contains(((WidgetCorner) i).getWidgetSource())) {
1432 enclosedWidgets.add(((WidgetCorner) i).getWidgetSource());
1433 }
1434 }
1435
1436 i.setHighlightMode(Item.HighlightMode.None);
1437 i.setHighlightColorToDefault();
1438 }
1439
1440 for (Widget iw : enclosedWidgets) {
1441 for (Item i : iw.getItems()) {
1442 if (!enclosed.contains(i)) {
1443 enclosed.add(i);
1444 }
1445 }
1446 }
1447
1448 return enclosed;
1449 }
1450
1451 /**
1452 * Gets the collection of Dot items that form the enclosure nearest to the
1453 * current mouse position.
1454 */
1455 public static Collection<Item> getEnclosingLineEnds() {
1456 return getEnclosingLineEnds(new Point(DisplayController.getMouseX(), DisplayController.getMouseY()));
1457 }
1458
1459 /**
1460 * Gets the collection of Dot items that form the enclosure nearest to the given
1461 * position.
1462 */
1463 public static Collection<Item> getEnclosingLineEnds(Point position) {
1464 // update enclosed shapes
1465 Frame current = DisplayController.getCurrentFrame();
1466 if (current == null) {
1467 return null;
1468 }
1469 List<Item> items = current.getSortedItems();
1470
1471 // Remove all items that are connected to freeItems
1472 List<Item> freeItems = new ArrayList<Item>(FreeItems.getInstance());
1473 while (freeItems.size() > 0) {
1474 Item item = freeItems.get(0);
1475 Collection<Item> connected = item.getAllConnected();
1476 items.removeAll(connected);
1477 freeItems.removeAll(connected);
1478 }
1479
1480 List<Item> used = new ArrayList<Item>(0);
1481
1482 while (items.size() > 0) {
1483 Item i = items.get(0);
1484 items.remove(i);
1485 if (i.isEnclosed()) {
1486 PolygonBounds p = i.getEnclosedShape();
1487 if (p.contains(position)) {
1488 used.add(i);
1489 items.removeAll(i.getEnclosingDots());
1490 }
1491 }
1492 }
1493
1494 if (used.size() == 0) {
1495 return null;
1496 }
1497
1498 // if there is only one possibility, return it
1499 if (used.size() == 1) {
1500 return used.get(0).getEnclosingDots();
1501 // otherwise, determine which polygon is closest to the cursor
1502 } else {
1503 Collections.sort(used, new Comparator<Item>() {
1504 @Override
1505 public int compare(Item d1, Item d2) {
1506 PolygonBounds p1 = d1.getEnclosedShape();
1507 PolygonBounds p2 = d2.getEnclosedShape();
1508
1509 int closest = Integer.MAX_VALUE;
1510 int close2 = Integer.MAX_VALUE;
1511
1512 int mouseX = DisplayController.getMouseX();
1513 int mouseY = DisplayController.getMouseY();
1514
1515 for (int i = 0; i < p1.getPointCount(); i++) {
1516 int diff = Math.abs(p1.getPoint(i).getX() - mouseX) + Math.abs(p1.getPoint(i).getY() - mouseY);
1517 int diff2 = Integer.MAX_VALUE;
1518
1519 if (i < p2.getPointCount()) {
1520 diff2 = Math.abs(p2.getPoint(i).getX() - mouseX) + Math.abs(p2.getPoint(i).getY() - mouseY);
1521 }
1522
1523 if (diff < Math.abs(closest)) {
1524 close2 = closest;
1525 closest = diff;
1526 } else if (diff < Math.abs(close2)) {
1527 close2 = diff;
1528 }
1529
1530 if (diff2 < Math.abs(closest)) {
1531 close2 = closest;
1532 closest = -diff2;
1533 } else if (diff2 < Math.abs(close2)) {
1534 close2 = diff2;
1535 }
1536 }
1537
1538 if (closest > 0 && close2 > 0) {
1539 return -10;
1540 }
1541
1542 if (closest < 0 && close2 < 0) {
1543 return 10;
1544 }
1545
1546 if (closest > 0) {
1547 return -10;
1548 }
1549
1550 return 10;
1551 }
1552
1553 });
1554
1555 return used.get(0).getEnclosingDots();
1556 }
1557 }
1558
1559 // TODO Remove this method!!
1560 // Can just getItemsWithin be used?
1561 public static Collection<Item> getItemsEnclosedBy(Frame frame, PolygonBounds poly) {
1562 Collection<Item> contained = frame.getItemsWithin(poly);
1563
1564 Collection<Item> results = new LinkedHashSet<Item>(contained.size());
1565
1566 // check for correct permissions
1567 for (Item item : contained) {
1568 // if the item is on the frame
1569 if (item.getParent() == frame || item.getParent() == null) {
1570 // item.Permission = Permission.full;
1571 results.add(item);
1572 // otherwise, it must be on an overlay frame
1573 } else {
1574 for (Overlay overlay : frame.getOverlays()) {
1575 if (overlay.Frame == item.getParent()) {
1576 item.setOverlayPermission(overlay.permission);
1577 results.add(item);
1578 break;
1579 }
1580 }
1581 }
1582 }
1583
1584 return results;
1585 }
1586
1587 public static void CreateDefaultProfile(String profileFor, Frame profile) {
1588 CreateDefaultProfile(profileFor, profile, null, null);
1589 }
1590
1591 /**
1592 * Copies the content from the default profile to the specified profile.
1593 * @param profileFor Name of profile that is destination of copy.
1594 * @param profile Profile being setup.
1595 * @param specifiedTextSettings text settings to provide a default value for in the new profile
1596 * @param specifiedGenericSettings generic settings to provide a default value for in the new profile
1597 */
1598 public static void CreateDefaultProfile(String profileFor, Frame profile,
1599 Map<String, Setting> specifiedSettings, Map<String, Consumer<Frame>> notifyWhenGenerated) {
1600 // If this is already the default profile then nothing (other than setting
1601 // title) needs to be done.
1602 Text titleItem = profile.getTitleItem();
1603 Text title = titleItem;
1604 if (!profileFor.equals(UserSettings.DEFAULT_PROFILE_NAME)) {
1605 // If this profile is not the default profile, copy it from the default profile
1606 // instead of generating a new profile
1607 // (this allows the possibility of modifying the default profile and having any
1608 // new profiles get those modifications)
1609 Frame defaultFrame = FrameIO.LoadProfile(UserSettings.DEFAULT_PROFILE_NAME);
1610 if (defaultFrame == null) {
1611 try {
1612 // If we do not have a default to copy, create one.
1613 defaultFrame = FrameIO.CreateNewProfile(UserSettings.DEFAULT_PROFILE_NAME, null, null);
1614 } catch (InvalidFramesetNameException invalidNameEx) {
1615 MessageBay.errorMessage("Failed to create default profile named: "
1616 + UserSettings.DEFAULT_PROFILE_NAME + ". "
1617 + "Profile names must start and end with a letter and must contain only letters and numbers.");
1618 return;
1619 } catch (ExistingFramesetException existingFramesetEx) {
1620 MessageBay.errorMessage("Failed to create the desired default frameset: "
1621 + UserSettings.DEFAULT_PROFILE_NAME + ", "
1622 + "because it already exists. This should never happen as we shouldn't be asking to create it if it already exists.");
1623 return;
1624 }
1625 }
1626
1627 MessageBay.suppressMessages(true);
1628 int lastNumber = FrameIO.getLastNumber(defaultFrame.getFramesetName());
1629 for (int i = 1; i <= lastNumber; i++) {
1630 // Load in next default, if it doesn't exist continue loop.
1631 defaultFrame = FrameIO.LoadFrame(defaultFrame.getFramesetName() + i);
1632 if (defaultFrame == null) {
1633 continue;
1634 }
1635
1636 // Create the next next (currently blank) profile frame.
1637 // If there is frame gaps in the default (say if there is no 4.exp but there is
1638 // a 5.exp) then retain those gaps.
1639 // This way copied relative links work.
1640 while (profile.getNumber() < defaultFrame.getNumber()) {
1641 profile = FrameIO.CreateFrame(profile.getFramesetName(), null, null);
1642 }
1643 // Ensure we are working from a blank slate.
1644 profile.reset();
1645 profile.removeAllItems(profile.getAllItems());
1646
1647 // For each item on defaultFrame:
1648 // 1. Set all items to be relatively linked so once copied their links correctly
1649 // point to the frame on the created profile rather than the default profile.
1650 // 2. Copy item from defaultFrame to the current profile frame being
1651 // constructed.
1652 // 3. Replace settings values of copied items with those specified in
1653 // specifiedSettings (if present)
1654 for (Item item : defaultFrame.getAllItems()) {
1655 item.setRelativeLink();
1656 }
1657 profile.addAllItems(defaultFrame.getAllItems());
1658 if (i == 1 && titleItem != null) {
1659 titleItem.setText(profileFor + "'s Profile");
1660 }
1661 String category = profile.getTitle();
1662 List<String> settingsKeys = null;
1663 if (specifiedSettings != null) {
1664 settingsKeys = specifiedSettings.keySet().stream().filter(key ->
1665 key.startsWith(category)).collect(Collectors.toList());
1666 }
1667 if (settingsKeys != null) {
1668 for (String key: settingsKeys) {
1669 Setting setting = specifiedSettings.get(key);
1670 String name = setting.getName();
1671 Text representation = setting.generateRepresentation(name, profile.getFramesetName());
1672 Collection<Text> canditates = profile.getTextItems();
1673 canditates.removeIf(text -> !text.getText().startsWith(representation.getText().split(" ")[0]));
1674 canditates.forEach(text -> {
1675 Point backupPos = text.getPosition();
1676 Item.DuplicateItem(representation, text);
1677 text.setText(representation.getText());
1678 text.setPosition(backupPos);
1679 });
1680 }
1681 }
1682 if (notifyWhenGenerated != null && notifyWhenGenerated.containsKey(category)) {
1683 notifyWhenGenerated.get(category).accept(profile);
1684 }
1685 FrameIO.SaveFrame(profile);
1686 }
1687 MessageBay.suppressMessages(false);
1688 } else {
1689 title.setText("Default Profile Frame");
1690 int xPos = 300;
1691 int yPos = 100;
1692 Text t;
1693
1694 // Add documentation links
1695 File helpDirectory = new File(FrameIO.HELP_PATH);
1696 if (helpDirectory != null) {
1697 File[] helpFramesets = helpDirectory.listFiles();
1698 if (helpFramesets != null) {
1699
1700 // Add the title for the help index
1701 Text help = profile.addText(xPos, yPos, "@Expeditee Help", null);
1702 help.setSize(25);
1703 help.setFontStyle("Bold");
1704 help.setFamily("SansSerif");
1705 help.setColor(TemplateSettings.ColorWheel.get()[3]);
1706
1707 xPos += 25;
1708 System.out.println("Installing frameset: ");
1709
1710 boolean first_item = true;
1711 for (File helpFrameset : helpFramesets) {
1712 String framesetName = helpFrameset.getName();
1713 if (!FrameIO.isValidFramesetName(framesetName)) {
1714 continue;
1715 }
1716
1717 if (first_item) {
1718 System.out.print(" " + framesetName);
1719 first_item = false;
1720 } else {
1721 System.out.print(", " + framesetName);
1722 }
1723 System.out.flush();
1724
1725 Frame indexFrame = FrameIO.LoadFrame(framesetName + '1');
1726 // Look through the folder for help index pages
1727 if (indexFrame != null && ItemUtils.FindTag(indexFrame.getSortedItems(), "@HelpIndex") != null) {
1728 // yPos += spacing;
1729 yPos += 30;
1730 t = profile.addText(xPos, yPos, '@' + indexFrame.getFramesetName(), null);
1731 t.setLink(indexFrame.getName());
1732 t.setColor(Colour.GREY);
1733 }
1734 }
1735 System.out.println();
1736 }
1737 }
1738
1739 xPos = 50;
1740 yPos = 100;
1741
1742 // Populate Start Pages and Settings
1743 File framesetDirectory = new File(FrameIO.FRAME_PATH);
1744
1745 if (framesetDirectory.exists()) {
1746 File[] startpagesFramesets = framesetDirectory.listFiles();
1747
1748 if (startpagesFramesets != null) {
1749 // Add Start Page title
1750 Text templates = profile.addText(xPos, yPos, "@Start Pages", null);
1751 templates.setSize(25);
1752 templates.setFontStyle("Bold");
1753 templates.setFamily("SansSerif");
1754 templates.setColor(TemplateSettings.ColorWheel.get()[3]);
1755
1756 xPos += 25;
1757
1758 // Start Pages should be the first frame in its own frameset +
1759 // frameset name should be present in FrameUtils.startPages[].
1760 for (File startpagesFrameset : startpagesFramesets) {
1761 String framesetName = startpagesFrameset.getName();
1762
1763 // Only add link if frameset is a startpage
1764 for (int i = 0; i < startPages.length; i++) {
1765 if (framesetName.equals(startPages[i])) {
1766 Frame indexFrame = FrameIO.LoadFrame(framesetName + '1');
1767
1768 // Add start page link
1769 if (indexFrame != null) {
1770 yPos += 30;
1771 t = profile.addText(xPos, yPos, '@' + indexFrame.getFramesetName(), null);
1772 t.setLink(indexFrame.getName());
1773 t.setColor(Colour.GREY);
1774 }
1775 }
1776 }
1777 }
1778 }
1779 }
1780
1781 FrameIO.SaveFrame(profile);
1782
1783 // Populate settings frameset
1784 Settings.Init();
1785 t = profile.addText(550, 100, "@Settings", null);
1786 t.setSize((float) 25.0);
1787 t.setFamily("SansSerif");
1788 t.setFontStyle("Bold");
1789 t.setColor(Colour.GREY);
1790 Settings.generateSettingsTree(t);
1791 System.out.println("@Settings: Default settings generation complete.");
1792
1793 FrameIO.SaveFrame(profile);
1794 }
1795 }
1796
1797 private static void checkTDFCItemWaiting(Frame currentFrame) {
1798 Item tdfcItem = FrameUtils.getTdfcItem();
1799 // if there is a TDFC Item waiting
1800 if (tdfcItem != null) {
1801 boolean change = currentFrame.hasChanged();
1802 boolean saved = currentFrame.isSaved();
1803 // Save the parent of the item if it has not been saved
1804 if (!change && !saved) {
1805 tdfcItem.setLink(null);
1806 tdfcItem.getParent().setChanged(true);
1807 FrameIO.SaveFrame(tdfcItem.getParent());
1808 DisplayController.requestRefresh(true);
1809 } else {
1810 SessionStats.CreatedFrame();
1811 }
1812
1813 setTdfcItem(null);
1814 }
1815 }
1816
1817 public static void setTdfcItem(Item _tdfcItem) {
1818 FrameUtils._tdfcItem = _tdfcItem;
1819 }
1820
1821 public static Item getTdfcItem() {
1822 return FrameUtils._tdfcItem;
1823 }
1824
1825 public static void setLastEdited(Text lastEdited) {
1826 // If the lastEdited is being changed then check if its @i
1827 Frame toReparse = null;
1828 Frame toRecalculate = null;
1829 Frame toUpdateObservers = null;
1830
1831 if (LastEdited == null) {
1832 // System.out.print("N");
1833 } else if (LastEdited != null) {
1834 // System.out.print("T");
1835 Frame parent = LastEdited.getParentOrCurrentFrame();
1836
1837 if (lastEdited != LastEdited) {
1838 if (LastEdited.startsWith("@i")) {
1839 // Check if its an image that can be resized to fit a box
1840 // around it
1841 String text = LastEdited.getText();
1842 if (text.startsWith("@i:") && !Character.isDigit(text.charAt(text.length() - 1))) {
1843 Collection<Item> enclosure = FrameUtils.getEnclosingLineEnds(LastEdited.getPosition());
1844 if (enclosure != null) {
1845 for (Item i : enclosure) {
1846 if (i.isLineEnd() && i.isEnclosed()) {
1847 DisplayController.getCurrentFrame().removeAllItems(enclosure);
1848 AxisAlignedBoxBounds rect = i.getEnclosedBox();
1849 LastEdited.setText(LastEdited.getText() + " " + Math.round(rect.getWidth()));
1850 LastEdited.setPosition(rect.getTopLeft());
1851 LastEdited.setThickness(i.getThickness());
1852 LastEdited.setBorderColor(i.getColor());
1853 break;
1854 }
1855 }
1856 StandardGestureActions.deleteItems(enclosure, false);
1857 }
1858 }
1859 toReparse = parent;
1860 } else if (LastEdited.recalculateWhenChanged()) {
1861 toRecalculate = parent;
1862 }
1863
1864 if (parent.hasObservers()) {
1865 toUpdateObservers = parent;
1866 }
1867 // Update the formula if in XRay mode
1868 if (DisplayController.isXRayMode() && LastEdited.hasFormula()) {
1869 LastEdited.setFormula(LastEdited.getText());
1870 }
1871 }
1872 if (lastEdited != LastEdited && LastEdited.getText().length() == 0 && LastEdited.getMinWidth() == null) {
1873 parent.removeItem(LastEdited);
1874 }
1875 }
1876 LastEdited = lastEdited;
1877
1878 if (!DisplayController.isXRayMode()) {
1879 if (toReparse != null) {
1880 Parse(toReparse, false, false);
1881 } else {
1882 if (toRecalculate != null) {
1883 toRecalculate.recalculate();
1884 }
1885
1886 if (toUpdateObservers != null) {
1887 toUpdateObservers.notifyObservers(false);
1888 }
1889 }
1890 }
1891 }
1892
1893 /**
1894 * Extracts files/folders from assets/resources-public and assets/resources-private
1895 * to {Expeditee Home}/resources-public and {Expeditee Home}/resources-private respectively.
1896 * @param force if true, resources will be extracted even ifthey have already been extracted before.
1897 */
1898 public static void extractResources(boolean force) {
1899 // Ensure groups area exists
1900 if (UserSettings.PublicAndPrivateResources) {
1901 // Extract private resources
1902 Path resourcesPrivate = Paths.get(FrameIO.PARENT_FOLDER).resolve("resources-private");
1903 extractResources("org/expeditee/assets/resources-private", resourcesPrivate, force);
1904
1905 // Extract public resources
1906 Path resourcesPublic = Paths.get(FrameIO.PARENT_FOLDER).resolve("resources-public");
1907 extractResources("org/expeditee/assets/resources-public", resourcesPublic, force);
1908 } else if (AuthenticatorBrowser.isAuthenticationRequired()) {
1909 // Deal with the instance of being in the old regime but using authentication.
1910
1911 // Ensure additional framesets
1912 Path framesetsDir = Paths.get(FrameIO.FRAME_PATH);
1913 boolean extracted = extractResources("org/expeditee/assets/resources-public/framesets", framesetsDir, force);
1914
1915 // Ensure additional images
1916 Path imagesDir = Paths.get(FrameIO.IMAGES_PATH);
1917 extracted |= extractResources("org/expeditee/assets/resources-public/images", imagesDir, force);
1918
1919 // Ensure deaddrops area exists
1920 extracted |= Paths.get(FrameIO.PARENT_FOLDER).resolve("deaddrops").toFile().mkdir();
1921
1922 if (extracted) {
1923 @SuppressWarnings("resource")
1924 Scanner in = new Scanner(System.in);
1925 System.out.println("Extracting resources...In order to use authentication, you need a new default profile frameset.");
1926 System.out.println("This is a destructive process, your existing default profile frameset (if it exists) will be deleted.");
1927 System.out.print("Do you want to proceed? y/N:");
1928 System.out.flush();
1929 String answer = in.nextLine();
1930 if (!answer.toLowerCase().startsWith("y")) {
1931 System.out.println("Exiting...");
1932 System.exit(1);
1933 }
1934
1935 // Ensure the default profile is 'update to date'.
1936 //NB: this limits the potential for those running old regime with authentication the ability to customise the default profile.
1937 Path defaultProfile = Paths.get(FrameIO.PROFILE_PATH).resolve("default");
1938 try {
1939 Files.walkFileTree(defaultProfile, new FileVisitor<Path>() {
1940 @Override
1941 public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
1942 dir.toFile().delete();
1943 return FileVisitResult.CONTINUE;
1944 }
1945 @Override
1946 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
1947 return FileVisitResult.CONTINUE;
1948 }
1949 @Override
1950 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
1951 file.toFile().delete();
1952 return FileVisitResult.CONTINUE;
1953 }
1954 @Override
1955 public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
1956 return FileVisitResult.CONTINUE;
1957 }
1958 });
1959 } catch (IOException e) {
1960 e.printStackTrace();
1961 }
1962 }
1963 }
1964 }
1965
1966 private static boolean extractResources(String source, Path destination, boolean force) {
1967 // If resources have already been extracted, and we are not forcing the extraction, there is nothing to do.
1968 if (!force && destination.resolve(".res").toFile().exists()) {
1969 return false;
1970 }
1971
1972 System.out.println("Extracting/Installing resources to: " + destination.getFileName());
1973
1974 // Create the destination
1975 destination.getParent().toFile().mkdirs();
1976
1977 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
1978 URL resourceUrl = classLoader.getResource(source);
1979 if (resourceUrl.getProtocol().equals("jar")) {
1980 try {
1981 JarURLConnection ju_connection = (JarURLConnection) resourceUrl.openConnection();
1982 JarFile jf = ju_connection.getJarFile();
1983 Enumeration<JarEntry> jarEntries = jf.entries();
1984 extractFromJarFile(classLoader, jarEntries, source, destination);
1985 } catch (IOException e) {
1986 System.err.println("Error: FrameUtils::extractResources. Exception whilst extracting resources from Jar File. Message: " + e.getMessage());
1987 }
1988 } else if (resourceUrl.getProtocol().equals("bundleresource")) {
1989 try {
1990 URLConnection urlConnection = resourceUrl.openConnection();
1991 Class<?> c = urlConnection.getClass();
1992 java.lang.reflect.Method toInvoke = c.getMethod("getFileURL");
1993 URL fileURL = (URL) toInvoke.invoke(urlConnection);
1994 extractResourcesFromFolder(new File(fileURL.getPath()), source, destination);
1995 } catch (IOException e) {
1996 System.err.println("Error: FrameUtils::extractResources. Problem opening connection to bundleresource. Message: " + e.getMessage());
1997 } catch (NoSuchMethodException e) {
1998 System.err.println("Error: FrameUtils::extractResources. Unable to find method URLConnection::getFileURL. Message: " + e.getMessage());
1999 } catch (InvocationTargetException e) {
2000 System.err.println("Error: FrameUtils::extractResources. Problem invoking URLConnection::getFileURL. Message: " + e.getMessage());
2001 } catch (IllegalAccessException e) {
2002 System.err.println("Error: FrameUtils::extractResources. Problem invoking URLConnection::getFileURL. Message: " + e.getMessage());
2003 }
2004 } else {
2005 try {
2006 File folder = new File(resourceUrl.toURI().getPath());
2007 extractResourcesFromFolder(folder, source, destination);
2008 } catch (URISyntaxException e) {
2009 System.err.println("Error: FrameUtils::extractResources. Problem converting URL to URI. Message: " + e.getMessage());
2010 } catch (IOException e) {
2011 System.err.println("Error: FrameUtils::extractResources. Exception whilst extracting resources from folder. Message: " + e.getMessage());
2012 }
2013 }
2014
2015 // Create the .res file to signal completion
2016 try {
2017 destination.resolve(".res").toFile().createNewFile();
2018 } catch (IOException e) {
2019 System.err.println("Error: FrameUtils::extractResources. Unable to create the .res file to flag that resources have been extracted. Message: " + e.getMessage());
2020 }
2021
2022 return true;
2023 }
2024
2025 private static void extractFromJarFile(ClassLoader classLoader, Enumeration<JarEntry> jarEntries, String source, Path destination) throws IOException {
2026 while (jarEntries.hasMoreElements()) {
2027 ZipEntry ze = jarEntries.nextElement();
2028 if (!ze.getName().startsWith(source)) {
2029 continue;
2030 }
2031 File out = destination.resolve(ze.getName().substring(source.length())).toFile();
2032 if (ze.isDirectory()) {
2033 out.mkdirs();
2034 continue;
2035 }
2036 FileOutputStream fOut = null;
2037 InputStream fIn = null;
2038 try {
2039 fOut = new FileOutputStream(out);
2040 fIn = classLoader.getResourceAsStream(ze.getName());
2041 byte[] bBuffer = new byte[1024];
2042 int nLen;
2043 while ((nLen = fIn.read(bBuffer)) > 0) {
2044 fOut.write(bBuffer, 0, nLen);
2045 }
2046 fOut.flush();
2047 } catch (Exception e) {
2048 e.printStackTrace();
2049 } finally {
2050 if (fOut != null) {
2051 fOut.close();
2052 }
2053 if (fIn != null) {
2054 fIn.close();
2055 }
2056 }
2057 }
2058 }
2059
2060 private static void extractResourcesFromFolder(File folder, String source, Path destination) throws IOException {
2061 LinkedList<File> items = new LinkedList<File>();
2062 items.addAll(Arrays.asList(folder.listFiles()));
2063 LinkedList<File> files = new LinkedList<File>();
2064
2065 while (items.size() > 0) {
2066 File file = items.remove(0);
2067 if (file.isFile()) {
2068 if (!file.getName().contains(".svn")) {
2069 files.add(file);
2070 }
2071 } else {
2072 if (!file.getName().contains(".svn")) {
2073 items.addAll(Arrays.asList(file.listFiles()));
2074 }
2075 }
2076 }
2077 for (File file : files) {
2078 String path = file.getPath();
2079 System.out.println(path);
2080 Path relativize = folder.toPath().relativize(Paths.get(file.getPath()));
2081 File out = destination.resolve(relativize).toFile();
2082 copyFile(file, out, true);
2083 }
2084 }
2085
2086 /**
2087 * @param src
2088 * @param dst
2089 * @throws IOException
2090 */
2091 public static void copyFile(File src, File dst, boolean overWrite) throws IOException {
2092 if (!overWrite && dst.exists()) {
2093 return;
2094 }
2095
2096 dst.getParentFile().mkdirs();
2097 FileOutputStream fOut = null;
2098 FileInputStream fIn = null;
2099 try {
2100 // System.out.println(out.getPath());
2101 fOut = new FileOutputStream(dst);
2102 fIn = new FileInputStream(src);
2103 byte[] bBuffer = new byte[1024];
2104 int nLen;
2105 while ((nLen = fIn.read(bBuffer)) > 0) {
2106 fOut.write(bBuffer, 0, nLen);
2107 }
2108 fOut.flush();
2109 } catch (Exception e) {
2110 e.printStackTrace();
2111 } finally {
2112 if (fOut != null) {
2113 fOut.close();
2114 }
2115 if (fIn != null) {
2116 fIn.close();
2117 }
2118 }
2119 }
2120
2121 public static Text getLastEdited() {
2122 return LastEdited;
2123 }
2124
2125 public static Collection<Text> getCurrentTextItems() {
2126 Collection<Text> currentTextItems = new LinkedHashSet<Text>();
2127 Collection<Item> currentItems = getCurrentItems(null);
2128 if (currentItems != null) {
2129 for (Item i : getCurrentItems(null)) {
2130 if (i instanceof Text && !i.isLineEnd()) {
2131 currentTextItems.add((Text) i);
2132 }
2133 }
2134 }
2135 return currentTextItems;
2136 }
2137}
Note: See TracBrowser for help on using the repository browser.