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

Last change on this file since 944 was 919, checked in by jts21, 10 years ago

Added license headers to all files, added full GPL3 license file, moved license header generator script to dev/bin/scripts

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