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

Last change on this file since 1064 was 1064, checked in by davidb, 8 years ago

Addition of new type of frame transition, with off-screen buffering to make use of alpha-values in transition more seamless; Enhancement of display-mode to be full screen; Enahcements to mouse interactions over images -- now if the mouse is over a pixel in the image that is the same colour as the background-color of the frame, it is treated as equivalent to being over the background, rather than the image item. Overall has a natural feel. You now need to click on 'non-trival' parts of an image to get image-related interactive Expteditee behaviour. (note: this is how text already behaves)

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