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

Last change on this file since 1200 was 1200, checked in by bln4, 6 years ago

org.expeditee.auth.gio.EncryptedExpReader ->
org.expeditee.auth.gio.EncryptedExpWriter ->
org.expeditee.io.DefaultFrameReader ->
org.expeditee.io.DefaultFrameWriter ->
org.expeditee.io.ExpReader ->
org.expeditee.io.ExpWriter ->

The beginnings of a authentication system for Expeditee. Frame files (.exp) can now be encrypted with a password. A login system is being developed to accompany this.


org.expeditee.gui.AttributeUtils ->
org.expeditee.gui.Frame ->
org.expeditee.gui.FrameUtils ->
org.expeditee.items.Item ->
org.expeditee.items.PermissionPair ->
org.expeditee.items.Text ->

As part of the development the login screen. Modifications to Text Items have been made. New properties are 'Mask' and 'MinWidth'. Mask allows you to set a character to use as...a mask...for example, a password field using '*'. MinWidth allows you to specify a minimum width for text boxes. A text box with a minimum width never shrinks below this width; even when it has no content.

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