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

Last change on this file since 1490 was 1490, checked in by bnemhaus, 4 years ago

FrameDirectory Settings can now have paths included in them that are not valid directories.
The check for valid directories now happens in the ResourceManager when the load request is made.

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