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

Last change on this file since 284 was 284, checked in by ra33, 16 years ago

Added additional mail functionality...
Also added REMINDER feature

File size: 43.2 KB
Line 
1package org.expeditee.gui;
2
3import java.awt.Color;
4import java.awt.Point;
5import java.awt.Polygon;
6import java.io.File;
7import java.util.ArrayList;
8import java.util.Collection;
9import java.util.Collections;
10import java.util.Comparator;
11import java.util.LinkedHashSet;
12import java.util.LinkedList;
13import java.util.List;
14
15import org.expeditee.agents.mail.MailSession;
16import org.expeditee.io.Logger;
17import org.expeditee.items.Circle;
18import org.expeditee.items.Dot;
19import org.expeditee.items.FrameBitmap;
20import org.expeditee.items.FrameImage;
21import org.expeditee.items.Item;
22import org.expeditee.items.ItemUtils;
23import org.expeditee.items.Line;
24import org.expeditee.items.Permission;
25import org.expeditee.items.Picture;
26import org.expeditee.items.Text;
27import org.expeditee.items.XRayable;
28import org.expeditee.items.widgets.InteractiveWidget;
29import org.expeditee.items.widgets.InteractiveWidgetInitialisationFailedException;
30import org.expeditee.items.widgets.InteractiveWidgetNotAvailableException;
31import org.expeditee.items.widgets.WidgetCorner;
32import org.expeditee.items.widgets.WidgetEdge;
33import org.expeditee.stats.SessionStats;
34
35public class FrameUtils {
36
37 private static final int COLUMN_WIDTH = 50;
38
39 /**
40 * Provides a way to monitor the time elapsed between button-down and the
41 * finished painting.
42 */
43 public static TimeKeeper ResponseTimer = new TimeKeeper();
44
45 private static float _ResponseTimeSum = 0;
46
47 private static float _LastResponse = 0;
48
49 private static Text LastEdited = null;
50
51 public static int MINIMUM_INTERITEM_SPACING = -6;
52
53 public static float getResponseTimeTotal() {
54 return _ResponseTimeSum;
55 }
56
57 public static float getLastResponseTime() {
58 return _LastResponse;
59 }
60
61 /**
62 * Checks if the given top Item is above the given bottom Item, allowing for
63 * the X coordinates to be off by a certain width...
64 *
65 * @param item1
66 * The Item to check is above the other Item
67 * @param item2
68 * The Item to check is below the top Item
69 * @return True if top is above bottom, False otherwise.
70 */
71 public static boolean inSameColumn(Item item1, Item item2) {
72 if (!(item1 instanceof Text) || !(item2 instanceof Text))
73 return false;
74
75 if (item1.getID() < 0 || item2.getID() < 0)
76 return false;
77
78 int minX = item2.getX();
79 int maxX = item2.getX() + item2.getBoundsWidth();
80
81 int startX = item1.getX();
82 int endX = item1.getX() + item1.getBoundsWidth();
83
84 // Check that the two items left values are close
85 if (Math.abs(item1.getX() - item2.getX()) > COLUMN_WIDTH)
86 return false;
87
88 // Ensure the two items
89 if ((minX >= startX && minX <= endX)
90 || (maxX >= startX && maxX <= endX)
91 || (startX >= minX && startX <= maxX)
92 || (endX >= minX && endX <= maxX))
93 return true;
94
95 return false;
96 }
97
98 public static boolean sameBulletType(String bullet1, String bullet2) {
99 if (bullet1 == null || bullet2 == null)
100 return false;
101
102 if (bullet1.equals("") || bullet2.equals(""))
103 return false;
104
105 if (Character.isLetter(bullet1.charAt(0))
106 && Character.isLetter(bullet2.charAt(0)))
107 return true;
108
109 if (Character.isDigit(bullet1.charAt(0))
110 && Character.isDigit(bullet2.charAt(0)))
111 return true;
112
113 // TODO make this more sofisticated
114
115 return false;
116 }
117
118 private static boolean needsRenumbering(String s) {
119 if (s == null || s.equals(""))
120 return false;
121 if (!Character.isLetterOrDigit(s.charAt(0)))
122 return false;
123
124 s = s.trim();
125 // if its all letters then we dont want to auto adjust
126 if (s.length() > 2) {
127 for (int i = 0; i < s.length() - 1; i++) {
128 if (!Character.isLetter(s.charAt(i)))
129 return true;
130 }
131 } else
132 return true;
133
134 return false;
135 }
136
137 /**
138 *
139 * @param toAlign
140 * @param moveAll
141 * @param adjust
142 * @return
143 */
144 public static int Align(List<Text> toAlign, boolean moveAll, int adjust) {
145 Collections.sort(toAlign);
146
147 // Single items dont need alignment
148 if (toAlign.size() < 2)
149 return 0;
150
151 // get the first item
152 Text from = toAlign.get(0);
153 if (from.getParent() == null)
154 from = toAlign.get(1);
155 int x = from.getX();
156
157 Frame curr = from.getParent();
158 Text above = curr.getTextAbove(from);
159
160 String lastBullet = "";
161
162 if (above != null && curr.isNormalTextItem(above))
163 lastBullet = FrameKeyboardActions.getAutoBullet(above.getText());
164 else {
165 lastBullet = FrameKeyboardActions.getBullet(toAlign.get(0)
166 .getText());
167 }
168 if (needsRenumbering(lastBullet)) {
169 // renumber...
170 for (int i = 0; i < toAlign.size(); i++) {
171
172 Text currentText = toAlign.get(i);
173 String currentBullet = FrameKeyboardActions
174 .getAutoBullet(currentText.getText());
175
176 if (sameBulletType(lastBullet, currentBullet)) {
177 currentText.stripFirstWord();
178
179 currentText.setText(lastBullet + currentText.getText());
180 lastBullet = FrameKeyboardActions.getAutoBullet(currentText
181 .getText());
182 }
183 }
184 }
185
186 // work out the spacing between the first item and the one above it
187
188 int space = 10 + adjust;
189
190 // if we are dropping from the title make the space a little bigger
191 // than normal
192
193 // If there are only two items get the gap from the start item on the
194 // zero frame if there is one
195 if (above == curr.getTitleItem()) {
196 Frame zero = FrameIO.LoadFrame(curr.getFramesetName() + '0');
197 String strGap = zero.getAnnotationValue("start");
198 if (strGap != null) {
199 try {
200 int gap = Integer.parseInt(strGap);
201 space = gap;
202 } catch (NumberFormatException nfe) {
203
204 }
205 }
206 } else if (above != null) {
207 // Make the gap between all items the same as the gap between
208 // the first two
209 space = (int) (from.getPolygon().getBounds().getMinY() - above
210 .getPolygon().getBounds().getMaxY());
211
212 if (space < MINIMUM_INTERITEM_SPACING)
213 space = MINIMUM_INTERITEM_SPACING;
214
215 if (above != curr.getNameItem() && above != curr.getTitleItem())
216 x = above.getX();
217
218 space += adjust;
219
220 from.setY((int) above.getPolygon().getBounds().getMaxY()
221 + space
222 + ((int) (from.getY() - from.getPolygon().getBounds()
223 .getMinY())));
224
225 if (moveAll)
226 from.setX(x);
227 }
228
229 for (int i = 1; i < toAlign.size(); i++) {
230 Item current = toAlign.get(i);
231 Item top = toAlign.get(i - 1);
232
233 // The bottom of the previous item
234 int bottom = (int) top.getPolygon().getBounds().getMaxY();
235
236 // the difference between the current item's Y coordinate and
237 // the top of the highlight box
238 int diff = (int) (current.getY() - current.getPolygon().getBounds()
239 .getMinY());
240
241 int newPos = bottom + space + diff;
242
243 if (moveAll) {
244 current.setPosition(x, newPos);
245 } else if (newPos > current.getY()) {
246 current.setY(newPos);
247 }
248
249 }
250
251 // if (insert != null)
252 // return insert.getY();
253
254 // Michael thinks we return the y value for the next new item??
255 int y = from.getY() + from.getBoundsHeight() + space;
256 return y;
257 }
258
259 public static boolean LeavingFrame(Frame current) {
260 checkTDFCItemWaiting(current);
261 // active overlay frames may also require saving if they have been
262 // changed
263 for (Overlay o : current.getOverlays())
264 if (!SaveCheck(o.Frame))
265 return false;
266
267 // if the check fails there is no point continuing
268 if (!SaveCheck(current))
269 return false;
270
271 for (Item i : current.getItems())
272 i.setHighlightMode(Item.HighlightMode.None);
273 return true;
274 }
275
276 private static boolean SaveCheck(Frame toSave) {
277 // don't bother saving frames that haven't changed
278 if (!toSave.hasChanged())
279 return true;
280
281 // if the frame has been changed, then save it
282 if (DisplayIO.isTwinFramesOn()) {
283 Frame opposite = DisplayIO.getOppositeFrame();
284
285 String side = "left";
286 if (DisplayIO.getCurrentSide() == 0)
287 side = "right";
288
289 // if the two frames both have changes, prompt the user for the
290 // next move
291 if (opposite.hasChanged() && opposite.equals(toSave)) {
292 if (DisplayIO.DisplayConfirmDialog(
293 "Leaving this frame will discard changes made in the "
294 + side + " Frame. Continue?", "Changes",
295 DisplayIO.TYPE_WARNING, DisplayIO.OPTIONS_OK_CANCEL,
296 DisplayIO.RESULT_OK)) {
297 FrameIO.SaveFrame(toSave);
298 DisplayIO.Reload(DisplayIO.FrameOnSide(opposite));
299 return true;
300 } else
301 return false;
302 } else if (opposite.hasOverlay(toSave)) {
303 if (toSave.hasChanged())
304 if (DisplayIO.DisplayConfirmDialog(
305 "Leaving this frame will discard changes made in the "
306 + side + " Frame. Continue?", "Changes",
307 DisplayIO.TYPE_WARNING,
308 DisplayIO.OPTIONS_OK_CANCEL, DisplayIO.RESULT_OK)) {
309 FrameIO.SaveFrame(toSave);
310 DisplayIO.Reload(DisplayIO.FrameOnSide(opposite));
311 return true;
312 } else
313 return false;
314 }
315
316 // save the current frame and restore the other side
317 FrameIO.SaveFrame(toSave);
318 return true;
319 }
320
321 // single-frame mode can just save and return
322 FrameIO.SaveFrame(toSave);
323 return true;
324 }
325
326 /**
327 * Displays the given Frame on the display. If the current frame has changed
328 * since the last save then it will be saved before the switch is made. The
329 * caller can also dictate whether the current frame is added to the
330 * back-stack or not.
331 *
332 * @param toDisplay
333 * The Frame to display on the screen
334 * @param addToBack
335 * True if the current Frame should be added to the back-stack,
336 * False otherwise
337 */
338 public static void DisplayFrame(Frame toDisplay, boolean addToBack) {
339 if (toDisplay == null)
340 return;
341
342 Frame current = DisplayIO.getCurrentFrame();
343
344 // Dont need to do anything if the frame to display is already being
345 // displayed
346 if (current.equals(toDisplay))
347 return;
348
349 // move any anchored connected items
350 if (FreeItems.itemsAttachedToCursor()) {
351 List<Item> toAdd = new ArrayList<Item>();
352 List<Item> toCheck = new ArrayList<Item>(FreeItems.getInstance());
353
354 while (toCheck.size() > 0) {
355 Item i = toCheck.get(0);
356 Collection<Item> connected = i.getAllConnected();
357
358 // // Only move completely enclosed items
359 // if (!toCheck.containsAll(connected)) {
360 // connected.retainAll(FreeItems.getInstance());
361 // FreeItems.getInstance().removeAll(connected);
362 // toCheck.removeAll(connected);
363 // FrameMouseActions.anchor(connected);
364 // } else {
365 // toCheck.removeAll(connected);
366 // }
367
368 // Anchor overlay items where they belong
369 if (i.getParent() != null && i.getParent() != current) {
370 FreeItems.getInstance().removeAll(connected);
371 toCheck.removeAll(connected);
372 FrameMouseActions.anchor(connected);
373 } else {
374 // Add stuff that is partially enclosed
375 // remove all the connected items from our list to check
376 toCheck.removeAll(connected);
377 // Dont add the items that are free
378 connected.removeAll(FreeItems.getInstance());
379 toAdd.addAll(connected);
380 }
381 }
382
383 current.removeAllItems(toAdd);
384
385 boolean oldChange = toDisplay.hasChanged();
386 toDisplay.updateIDs(toAdd);
387 toDisplay.addAllItems(toAdd);
388 toDisplay.setChanged(oldChange);
389 }
390
391 if (addToBack && current != toDisplay) {
392 FrameIO.checkTDFC(current);
393 }
394
395 // if the saving happened properly, we can continue
396 if (!LeavingFrame(current)) {
397 MessageBay.displayMessage("Navigation cancelled");
398 return;
399 }
400
401 if (addToBack && current != toDisplay) {
402 DisplayIO.addToBack(current);
403 }
404
405 Parse(toDisplay);
406 DisplayIO.setCurrentFrame(toDisplay, addToBack);
407 FrameMouseActions.updateCursor();
408 // FrameMouseActions.getInstance().refreshHighlights();
409 // update response timer
410 _LastResponse = ResponseTimer.getElapsedSeconds();
411 _ResponseTimeSum += _LastResponse;
412 DisplayIO.UpdateTitle();
413 }
414
415 /**
416 * Loads and displays the Frame with the given framename, and adds the
417 * current frame to the back-stack if required.
418 *
419 * @param framename
420 * The name of the Frame to load and display
421 * @param addToBack
422 * True if the current Frame should be added to the back-stack,
423 * false otherwise
424 */
425 public static void DisplayFrame(String frameName, boolean addToBack) {
426 Frame newFrame = getFrame(frameName);
427
428 if (newFrame != null)
429 // display the frame
430 DisplayFrame(newFrame, addToBack);
431 }
432
433 /**
434 * Loads and displays the Frame with the given framename and adds the
435 * current frame to the back-stack. This is the same as calling
436 * DisplayFrame(framename, true)
437 *
438 * @param framename
439 * The name of the Frame to load and display
440 */
441 public static void DisplayFrame(String framename) {
442 DisplayFrame(framename, true);
443 }
444
445 public static void DisplayHomeFrame() {
446 DisplayFrame(UserSettings.FirstFrame);
447 }
448
449 public static void DisplayProfileFrame() {
450 DisplayFrame(UserSettings.Username + '1');
451 }
452
453 public static Frame getFrame(String frameName) {
454 // if the new frame does not exist then tell the user
455 Frame f = FrameIO.LoadFrame(frameName);
456
457 if (f == null) {
458 MessageBay.errorMessage("Frame '" + frameName
459 + "' could not be found.");
460 }
461
462 return f;
463 }
464
465 /**
466 * Creates a new Picture Item from the given Text source Item and adds it to
467 * the given Frame.
468 *
469 * @return True if the image was created successfully, false otherwise
470 */
471 private static boolean createPicture(Frame frame, Text txt) {
472 // attempt to create the picture
473 Picture pic = ItemUtils.CreatePicture(txt, frame);
474
475 // if the picture could not be created successfully
476 if (pic == null) {
477 String imagePath = txt.getText();
478 assert (imagePath != null);
479 imagePath = new AttributeValuePair(imagePath).getValue().trim();
480 if (imagePath.length() == 0) {
481 return false;
482 // MessageBay.errorMessage("Expected image path after @i:");
483 } else {
484 MessageBay.errorMessage("Image " + imagePath
485 + " could not be loaded");
486 }
487 return false;
488 }
489 frame.addItem(pic);
490
491 return true;
492 }
493
494 /**
495 * Creates an interactive widget and adds it to a frame. If txt has no
496 * parent the parent will be set to frame.
497 *
498 * @param frame
499 * Frame to add widget to. Must not be null.
500 *
501 * @param txt
502 * Text to create the widget from. Must not be null.
503 *
504 * @return True if created/added. False if coul not create.
505 *
506 * @author Brook Novak
507 */
508 private static boolean createWidget(Frame frame, Text txt) {
509
510 if (frame == null)
511 throw new NullPointerException("frame");
512 if (txt == null)
513 throw new NullPointerException("txt");
514
515 // Safety
516 if (txt.getParent() == null)
517 txt.setParent(frame);
518
519 InteractiveWidget iw = null;
520
521 try {
522
523 iw = InteractiveWidget.createWidget(txt);
524
525 } catch (InteractiveWidgetNotAvailableException e) {
526 e.printStackTrace();
527 MessageBay.errorMessage("Cannot create iWidget: " + e.getMessage());
528 } catch (InteractiveWidgetInitialisationFailedException e) {
529 e.printStackTrace();
530 MessageBay.errorMessage("Cannot create iWidget: " + e.getMessage());
531 } catch (IllegalArgumentException e) {
532 e.printStackTrace();
533 MessageBay.errorMessage("Cannot create iWidget: " + e.getMessage());
534 }
535
536 if (iw == null)
537 return false;
538
539 frame.removeItem(txt);
540
541 frame.addAllItems(iw.getItems());
542
543 return true;
544 }
545
546 public static Collection<String> ParseProfile(Frame profile) {
547 Collection<String> errors = new LinkedList<String>();
548 if (profile == null)
549 return errors;
550
551 UserSettings.TitleTemplate = profile.getTitleItem();
552
553 // check for all tags setting user values
554 for (Text item : profile.getBodyTextItems(false)) {
555
556 AttributeValuePair avp = new AttributeValuePair(item.getText());
557 String attributeFullCase = avp.getAttributeOrValue();
558
559 if (attributeFullCase == null)
560 continue;
561 String attribute = attributeFullCase.toLowerCase();
562
563 if (attribute.equals("homeframe")) {
564 String first = getLink(item, UserSettings.FirstFrame);
565 // do not use non-existant frames as the first frame
566 if (FrameIO.isValidFrameName(first)) {
567 UserSettings.FirstFrame = first;
568 }
569 // warn the user
570 else {
571 // MessageBay.warningMessage("Home frame: " + first
572 // + " is not a valid frame.");
573 UserSettings.FirstFrame = profile.getName();
574 }
575 } else if (attribute.equals("menuframe"))
576 UserSettings.MenuFrame = getLink(item, UserSettings.MenuFrame);
577 else if (attribute.equals("defaultframe"))
578 UserSettings.DefaultFrame = getLink(item,
579 UserSettings.DefaultFrame);
580 else if (attribute.equals("mailsettings"))
581 MailSession.init(item.getChild());
582 else if (attribute.equals("reminders"))
583 Reminders.init(item.getChild());
584 else if (attribute.equals("antialias"))
585 UserSettings.AntiAlias = avp.getBooleanValue();
586 else if (attribute.equals("gravity"))
587 UserSettings.Gravity = avp.getIntegerValue();
588 else if (attribute.equals("showlinehighlight"))
589 UserSettings.LineHighlight = avp.getBooleanValue();
590 else if (attribute.equals("linestraightenthreshold"))
591 UserSettings.LineStraightenThreshold = avp.getIntegerValue();
592 else if (attribute.equals("noopthreshold"))
593 UserSettings.NoOpThreshold = avp.getIntegerValue();
594 else if (attribute.equals("initialwidth")) {
595 UserSettings.InitialWidth = avp.getIntegerValue();
596 } else if (attribute.equals("initialheight")) {
597 UserSettings.InitialHeight = avp.getIntegerValue();
598 } else if (attribute.equals("titleposition")) {
599 UserSettings.TitlePosition = avp.getIntegerValue();
600 } else if (attribute.equals("logging")) {
601 UserSettings.Logging = avp.getBooleanValue();
602 } else if (attribute.equals("itemtemplate")) {
603 UserSettings.ItemTemplate = ((Text) item).getTemplateForm();
604 } else if (attribute.equals("annotationtemplate")) {
605 UserSettings.AnnotationTemplate = ((Text) item)
606 .getTemplateForm();
607 } else if (attribute.equals("statstemplate")) {
608 UserSettings.StatTemplate = ((Text) item).getTemplateForm();
609 } else if (attribute.equals("commenttemplate")) {
610 UserSettings.CodeCommentTemplate = ((Text) item)
611 .getTemplateForm();
612 } else if (attribute.equals("dottemplate")) {
613 UserSettings.DotTemplate = ((Text) item).getTemplateForm();
614 } else if (attribute.equals("framesetdir")) {
615 String dir = getDir(avp.getValue());
616 if (dir != null) {
617 UserSettings.FrameDirs.add(dir);
618 }
619 } else if (attribute.equals("logdir")) {
620 org.expeditee.gui.FrameIO.LOGS_DIR = getDir(avp.getValue());
621 } else if (attribute.equals("imagedir")) {
622 String dir = getDir(avp.getValue());
623 if (dir != null)
624 UserSettings.ImageDirs.add(0, dir);
625 } else if (attribute.equals("threading")) {
626 UserSettings.Threading = avp.getBooleanValue();
627 } else if (attribute.equals("colorwheel")) {
628 Item.COLOR_WHEEL = getColorWheel(item);
629 } else if (attribute.equals("fillcolorwheel")) {
630 Item.FILL_COLOR_WHEEL = getColorWheel(item);
631 } else if (attribute.equals("backgroundcolorwheel")) {
632 Frame.COLOR_WHEEL = getColorWheel(item);
633 } else if (attribute.equals("framesetdirs")) {
634 UserSettings.FrameDirs.addAll(getDirs(item));
635 } else {
636 errors.add(attributeFullCase + " is not a profile frame tag");
637 }
638 }
639 return errors;
640 }
641
642 public static void loadFirstFrame(Frame profile) {
643 if (UserSettings.FirstFrame == null)
644 UserSettings.FirstFrame = profile.getName();
645 // else {
646 Frame firstFrame = FrameIO.LoadFrame(UserSettings.FirstFrame);
647 if (firstFrame == null) {
648 MessageBay.warningMessage("Home frame not found: "
649 + UserSettings.FirstFrame);
650 UserSettings.FirstFrame = profile.getName();
651 DisplayIO.setCurrentFrame(profile, true);
652 } else {
653 DisplayIO.setCurrentFrame(firstFrame, true);
654 }
655 // }
656 }
657
658 private static Color[] getColorWheel(Item item) {
659 Frame child = item.getChild();
660 if (child != null) {
661 List<Text> textItems = child.getBodyTextItems(false);
662 Color[] colorList = new Color[textItems.size() + 1];
663 for (int i = 0; i < textItems.size(); i++) {
664 colorList[i] = textItems.get(i).getColor();
665 }
666 // Make the last item transparency or default for forecolor
667 colorList[colorList.length - 1] = null;
668
669 return colorList;
670 }
671 return new Color[] { Color.black, Color.white, null };
672 }
673
674 private static String getLink(Item item, String alt) {
675 if (item == null || !(item instanceof Text))
676 return alt;
677
678 String value = new AttributeValuePair(item.getText()).getValue().trim();
679 if (value.length() > 0) {
680 item.setLink(value);
681 return value;
682 } else if (item.getLink() != null)
683 return item.getLink();
684
685 return alt;
686 }
687
688 private static String getDir(String name) {
689 if (name != null) {
690 File tester = new File(name);
691 if (tester.exists() && tester.isDirectory()) {
692 if (name.endsWith(File.separator))
693 return name;
694 else
695 return name + File.separator;
696 } else {
697 MessageBay.errorMessage("Directory not found: " + name);
698 }
699 } else {
700 MessageBay.warningMessage("Missing value for profile attribute"
701 + name);
702 }
703 return null;
704 }
705
706 private static ArrayList<String> getDirs(Item item) {
707 ArrayList<String> dirsToAdd = new ArrayList<String>();
708 String dirListFrameName = item.getAbsoluteLink();
709 if (dirListFrameName != null) {
710 Frame dirListFrame = FrameIO.LoadFrame(dirListFrameName);
711 if (dirListFrame != null) {
712 for (Text t : dirListFrame.getBodyTextItems(false)) {
713 String dirName = t.getText().trim();
714 File tester = new File(dirName);
715 if (tester.exists() && tester.isDirectory()) {
716 if (dirName.endsWith(File.separator))
717 dirsToAdd.add(dirName);
718 else
719 dirsToAdd.add(dirName + File.separator);
720 }
721 }
722 }
723 }
724
725 return dirsToAdd;
726 }
727
728 public static void Parse(Frame toParse) {
729 Parse(toParse, false);
730 }
731
732 /**
733 * Checks for any special Annotation items and updates the display as
734 * necessary. Special Items: Images, overlays, sort.
735 *
736 */
737 public static void Parse(Frame toParse, boolean firstParse) {
738 Parse(toParse, firstParse, false);
739 }
740
741 /**
742 *
743 * @param toParse
744 * @param firstParse
745 * @param ignoreAnnotations
746 * used to prevent infinate loops such as when performing TDFC
747 * with an ao tag linked to a frame with an frameImage of a frame
748 * which also has an ao tag on it.
749 */
750 public static void Parse(Frame toParse, boolean firstParse,
751 boolean ignoreAnnotations) {
752 // TODO check why we are getting toParse == null... when profile frame
753 // is being created and change the lines below
754 if (toParse == null)
755 return;
756 // System.out.println(firstParse);
757 if (firstParse)
758 ItemUtils.EnclosedCheck(toParse.getItems());
759 List<Item> items = toParse.getItems();
760
761 // if XRayMode is on, replace pictures with their underlying text
762 if (FrameGraphics.isXRayMode()) {
763
764 // BROOK: Must handle these a little different
765 List<InteractiveWidget> widgets = toParse.getInteractiveWidgets();
766
767 for (Item i : items) {
768 if (i instanceof XRayable) {
769 toParse.removeItem(i);
770 // Show the items
771 for (Item item : ((XRayable) i).getConnected()) {
772 item.setVisible(true);
773 item.removeEnclosure(i);
774 }
775 } else if (i instanceof WidgetCorner) {
776 toParse.removeItem(i);
777 } else if (i instanceof WidgetEdge) {
778 toParse.removeItem(i);
779 } else if (i.hasFormula()) {
780 i.setText(i.getFormula());
781 }
782 }
783
784 for (InteractiveWidget iw : widgets) {
785 toParse.addItem(iw.getSource());
786 }
787 }
788
789 // Text title = null;
790 // Text template = UserSettingsTemplate.copy();
791
792 List<Overlay> overlays = new ArrayList<Overlay>();
793 List<Vector> vectors = new ArrayList<Vector>();
794
795 // disable reading of cached overlays if in twinframes mode
796 if (DisplayIO.isTwinFramesOn())
797 FrameIO.SuspendCache();
798
799 int pointtype = Item.POINTTYPE_SQUARE;
800 boolean filledPoints = true;
801
802 Permission permission = toParse.getPermission();
803 toParse.clearAnnotations();
804
805 // check for any new overlay items
806 for (Item i : toParse.getItems()) {
807 try {
808 i.setPermission(permission);
809 if (i instanceof WidgetCorner) {
810 // TODO improve efficiency so it only updates once... using
811 // observer design pattern
812 i.update();
813 } else if (i instanceof Text) {
814 if (i.isAnnotation()) {
815 if (ItemUtils.startsWithTag(i, ItemUtils.TAG_POINTTYPE)) {
816 Text txt = (Text) i;
817 String line = txt.getFirstLine();
818 line = ItemUtils.StripTag(line, ItemUtils
819 .GetTag(ItemUtils.TAG_POINTTYPE));
820
821 if (line != null) {
822 line = line.toLowerCase();
823 if (line.indexOf(" ") > 0) {
824 String fill = line.substring(line
825 .indexOf(" ") + 1);
826 if (fill.startsWith("nofill"))
827 filledPoints = false;
828 else
829 filledPoints = true;
830 }
831
832 if (line.startsWith("circle"))
833 pointtype = Item.POINTTYPE_CIRCLE;
834 else
835 pointtype = Item.POINTTYPE_SQUARE;
836 }
837 }// check for new VECTOR items
838 else if (!FrameGraphics.isXRayMode()
839 && ItemUtils.startsWithTag(i,
840 ItemUtils.TAG_VECTOR)
841 && i.getLink() != null) {
842 addVector(vectors, Permission.none, permission, i);
843 } else if (!FrameGraphics.isXRayMode()
844 && ItemUtils.startsWithTag(i,
845 ItemUtils.TAG_ACTIVE_VECTOR)
846 && i.getLink() != null) {
847 addVector(vectors, Permission.followLinks,
848 permission, i);
849 }
850 // check for new OVERLAY items
851 else if (!ignoreAnnotations && ItemUtils.startsWithTag(i,
852 ItemUtils.TAG_OVERLAY)
853 && i.getLink() != null) {
854 if (i.getAbsoluteLink().equalsIgnoreCase(
855 toParse.getName())) {
856 // This frame contains an active overlay which
857 // points to itself
858 MessageBay
859 .errorMessage(toParse.getName()
860 + " contains an @o which links to itself");
861 continue;
862 }
863
864 Frame overlayFrame = FrameIO.LoadFrame(i
865 .getAbsoluteLink());
866 // Parse(overlay);
867 if (overlayFrame != null
868 && Overlay.getOverlay(overlays,
869 overlayFrame) == null)
870 overlays.add(new Overlay(overlayFrame,
871 Permission.none));
872 }
873 // check for ACTIVE_OVERLAY items
874 else if (!ignoreAnnotations && ItemUtils.startsWithTag(i,
875 ItemUtils.TAG_ACTIVE_OVERLAY)
876 && i.getLink() != null) {
877 String link = i.getAbsoluteLink();
878 if (link.equalsIgnoreCase(toParse.getName())) {
879 // This frame contains an active overlay which
880 // points to itself
881 MessageBay
882 .errorMessage(toParse.getName()
883 + " contains an @ao which links to itself");
884 continue;
885 }
886 Frame overlayFrame = FrameIO.LoadFrame(link);
887 // get level if specified
888 String level = new AttributeValuePair(i.getText())
889 .getValue();
890 // default permission (if none is specified)
891 Permission permissionLevel = Permission
892 .getPermission(level,
893 Permission.followLinks);
894
895 if (overlayFrame != null) {
896 Overlay existingOverlay = Overlay.getOverlay(
897 overlays, overlayFrame);
898 // If it wasnt in the list create it and add it.
899 if (existingOverlay == null) {
900 Overlay newOverlay = new Overlay(
901 overlayFrame, permissionLevel);
902 i.setOverlay(newOverlay);
903 overlays.add(newOverlay);
904 } else {
905 existingOverlay.Frame
906 .setPermission(permissionLevel);
907 }
908 }
909 }
910 // check for Images and widgets
911 else {
912 if (!FrameGraphics.isXRayMode()) {
913 if (ItemUtils.startsWithTag(i,
914 ItemUtils.TAG_IMAGE, true)) {
915 if (!i.hasEnclosures()) {
916 createPicture(toParse, (Text) i);
917 }
918 // check for frame images
919 } else if (ItemUtils.startsWithTag(i,
920 ItemUtils.TAG_FRAME_IMAGE)
921 && i.getLink() != null
922 && !i.getAbsoluteLink()
923 .equalsIgnoreCase(
924 toParse.getName())) {
925 XRayable image = null;
926 if (i.hasEnclosures()) {
927 // i.setHidden(true);
928 // image =
929 // i.getEnclosures().iterator().next();
930 // image.refresh();
931 } else {
932 image = new FrameImage((Text) i,
933 toParse, null);
934 }
935 // TODO Add the image when creating new
936 // FrameImage
937 toParse.addItem(image);
938 } else if (ItemUtils.startsWithTag(i,
939 ItemUtils.TAG_BITMAP_IMAGE)
940 && i.getLink() != null
941 && !i.getAbsoluteLink()
942 .equalsIgnoreCase(
943 toParse.getName())) {
944 XRayable image = null;
945 if (i.hasEnclosures()) {
946 // image =
947 // i.getEnclosures().iterator().next();
948 // image.refresh();
949 // i.setHidden(true);
950 } else {
951 // If a new bitmap is created for a
952 // frame which already has a bitmap dont
953 // recreate the bitmap
954 image = new FrameBitmap((Text) i,
955 toParse, null);
956 }
957 toParse.addItem(image);
958 } else if (ItemUtils.startsWithTag(i, "@c")) {
959 // Can only have a @c
960 if (!i.hasEnclosures()
961 && i.getLines().size() == 1) {
962 toParse.addItem(new Circle((Text) i));
963 }
964 // Check for interactive widgets
965 } else if (ItemUtils.startsWithTag(i,
966 ItemUtils.TAG_IWIDGET)) {
967 createWidget(toParse, (Text) i);
968 }
969 }
970 // TODO decide exactly what to do here!!
971 toParse.addAnnotation((Text) i);
972 }
973 } else if (!FrameGraphics.isXRayMode() && i.hasFormula()) {
974 i.calculate(i.getFormula());
975 }
976 }
977 } catch (Exception e) {
978 Logger.Log(e);
979 e.printStackTrace();
980 MessageBay.warningMessage("Exception occured when loading "
981 + i.getClass().getSimpleName() + "(ID: " + i.getID()
982 + ") " + e.getMessage());
983 }
984 }
985
986 for (Item i : items) {
987 if (i instanceof Dot) {
988 ((Dot) i).setPointType(pointtype);
989 ((Dot) i).useFilledPoints(filledPoints);
990 }
991 }
992
993 FrameIO.ResumeCache();
994
995 synchronized (toParse) { // BROOK: This does not appear to do
996 // anything! Their seems to be no
997 // thread-safe protocol for this project...
998 toParse.clearOverlays();
999 toParse.addAllOverlays(overlays);
1000 toParse.clearVectors();
1001 toParse.addAllVectors(vectors);
1002 }
1003 }
1004
1005 /**
1006 * @param vectors
1007 * @param permission
1008 * @param i
1009 */
1010 private static void addVector(List<Vector> vectors,
1011 Permission defaultPermission, Permission framePermission, Item i) {
1012 // TODO It is possible to get into an infinate loop if a
1013 // frame contains an @ao which leads to a frame with an
1014 // @v which points back to the frame with the @ao
1015 Frame vector = FrameIO.LoadFrame(i.getAbsoluteLink());
1016 // Get the permission from off the vector frame
1017 Permission vectorPermission = Permission.getPermission(vector
1018 .getAnnotationValue("permission"), defaultPermission);
1019 // If the frame permission is lower, use that
1020 vectorPermission = Permission.min(vectorPermission, framePermission);
1021 // Highest permissable permission for vectors is copy
1022 vectorPermission = Permission.min(vectorPermission, Permission.copy);
1023 if (vector != null) {
1024 String scaleString = new AttributeValuePair(i.getText()).getValue();
1025 Float scale = 1F;
1026 try {
1027 scale = Float.parseFloat(scaleString);
1028 } catch (Exception e) {
1029 }
1030 Vector newVector = new Vector(vector, vectorPermission, i
1031 .getPosition(), scale, i.getColor(), i.getBackgroundColor());
1032 i.setOverlay(newVector);
1033 vectors.add(newVector);
1034 }
1035 }
1036
1037 /**
1038 * Searches through the list of items on this frame to find one at the given
1039 * x,y coordinates.
1040 *
1041 * @param x
1042 * The x coordinate
1043 * @param y
1044 * The y coordinate
1045 * @return The Item at the given coordinates, or NULL if none is found.
1046 */
1047 public static Item onItem(Frame toCheck, float floatX, float floatY) {
1048 // System.out.println("MouseX: " + floatX + " MouseY: " + floatY);
1049 int x = Math.round(floatX);
1050 int y = Math.round(floatY);
1051 if (toCheck == null)
1052 return null;
1053
1054 List<Item> possibles = new ArrayList<Item>(0);
1055
1056 // if the mouse is in the message area
1057 if (y > FrameGraphics.getMaxFrameSize().getHeight()) {
1058 // check the individual message items
1059 for (Item message : MessageBay.getMessages()) {
1060 if (message != null) {
1061 if (message.contains(x, y)) {
1062 message.setPermission(Permission.copy);
1063 possibles.add(message);
1064 } else {
1065 // Not sure why but if the line below is removed then
1066 // several items can be highlighted at once
1067 message.setHighlightMode(Item.HighlightMode.None);
1068 }
1069 }
1070 }
1071
1072 // check the link to the message frame
1073 if (MessageBay.getMessageLink() != null) {
1074 if (MessageBay.getMessageLink().contains(x, y)) {
1075 MessageBay.getMessageLink().setPermission(Permission.copy);
1076 possibles.add(MessageBay.getMessageLink());
1077 }
1078 }
1079
1080 // this is taken into account in contains
1081 // y -= FrameGraphics.getMaxFrameSize().height;
1082 // otherwise, the mouse is on the frame
1083 } else {
1084 if (LastEdited != null && /*
1085 * LastEdited.isVisible() &&
1086 */LastEdited.contains(x, y)
1087 && !FreeItems.getInstance().contains(LastEdited)
1088 && LastEdited.getParent() == DisplayIO.getCurrentFrame()
1089 && LastEdited.getParent().getItems().contains(LastEdited)) {
1090 LastEdited.setPermission(Permission.full);
1091 return LastEdited;
1092 } else {
1093 setLastEdited(null);
1094 }
1095 ArrayList<Item> checkList = new ArrayList<Item>();
1096 checkList.addAll(toCheck.getAllItems());
1097 checkList.add(toCheck.getNameItem());
1098 for (Item i : checkList) {
1099 // do not check annotation items in audience mode
1100 if (i.isVisible()
1101 && !(FrameGraphics.isAudienceMode() && i.isAnnotation())) {
1102 if (i.contains(x, y)
1103 && !FreeItems.getInstance().contains(i)) {
1104 possibles.add(i);
1105 }
1106 }
1107 }
1108 }
1109
1110 // if there are no possible items, return null
1111 if (possibles.size() == 0)
1112 return null;
1113
1114 // if there is only one possibility, return it
1115 if (possibles.size() == 1)
1116 return possibles.get(0);
1117
1118 // return closest x,y pair to mouse
1119 Item closest = possibles.get(0);
1120 int distance = (int) Math.round(Math.sqrt(Math.pow(Math.abs(closest
1121 .getX()
1122 - x), 2)
1123 + Math.pow(Math.abs(closest.getY() - y), 2)));
1124
1125 for (Item i : possibles) {
1126 int d = (int) Math.round(Math.sqrt(Math.pow(Math.abs(i.getX() - x),
1127 2)
1128 + Math.pow(Math.abs(i.getY() - y), 2)));
1129
1130 // System.out.println(d);
1131 if (d <= distance) {
1132 distance = d;
1133
1134 // dots take precedence over lines
1135 if ((!(closest instanceof Dot && i instanceof Line))
1136 && (!(closest instanceof Text && i instanceof Line)))
1137 closest = i;
1138
1139 }
1140
1141 }
1142
1143 return closest;
1144 }
1145
1146 public synchronized static Item getCurrentItem() {
1147 return onItem(DisplayIO.getCurrentFrame(), DisplayIO.getMouseX(),
1148 FrameMouseActions.getY());
1149 }
1150
1151 public static Polygon getEnlosingPolygon() {
1152 Collection<Item> enclosure = getEnclosingLineEnds();
1153 if (enclosure == null || enclosure.size() == 0)
1154 return null;
1155
1156 return enclosure.iterator().next().getEnclosedShape();
1157 }
1158
1159 /**
1160 *
1161 * @param currentItem
1162 * @return
1163 */
1164 public static Collection<Item> getCurrentItems() {
1165 return getCurrentItems(getCurrentItem());
1166 }
1167
1168 public static Collection<Item> getCurrentItems(Item currentItem) {
1169
1170 Collection<Item> enclosure = getEnclosingLineEnds();
1171 if (enclosure == null || enclosure.size() == 0)
1172 return null;
1173
1174 Item firstItem = enclosure.iterator().next();
1175
1176 Collection<Item> enclosed = getItemsEnclosedBy(DisplayIO
1177 .getCurrentFrame(), firstItem.getEnclosedShape());
1178
1179 // Brook: enclosed widgets are to be fully enclosed, never partially
1180 List<InteractiveWidget> enclosedWidgets = new LinkedList<InteractiveWidget>();
1181 for (Item i : enclosed) {
1182 // Dont want to loose the highlighting from the current item
1183 if (i != currentItem && !enclosure.contains(i))
1184 i.setHighlightMode(Item.HighlightMode.None);
1185 if (i instanceof WidgetCorner) {
1186 if (!enclosedWidgets.contains(((WidgetCorner) i)
1187 .getWidgetSource()))
1188 enclosedWidgets.add(((WidgetCorner) i).getWidgetSource());
1189 }
1190 }
1191
1192 for (InteractiveWidget iw : enclosedWidgets) {
1193 for (Item i : iw.getItems()) {
1194 if (!enclosed.contains(i)) {
1195 enclosed.add(i);
1196 }
1197 }
1198 }
1199
1200 return enclosed;
1201 }
1202
1203 public static Collection<Item> getEnclosingLineEnds() {
1204 return getEnclosingLineEnds(new Point(DisplayIO.getMouseX(),
1205 FrameMouseActions.getY()));
1206 }
1207
1208 public static Collection<Item> getEnclosingLineEnds(Point position) {
1209 // update enclosed shapes
1210 Frame current = DisplayIO.getCurrentFrame();
1211 List<Item> items = current.getItems();
1212
1213 // Remove all items that are connected to freeItems
1214 List<Item> freeItems = new ArrayList<Item>(FreeItems.getInstance());
1215 while (freeItems.size() > 0) {
1216 Item item = freeItems.get(0);
1217 Collection<Item> connected = item.getAllConnected();
1218 items.removeAll(connected);
1219 freeItems.removeAll(connected);
1220 }
1221
1222 List<Item> used = new ArrayList<Item>(0);
1223
1224 while (items.size() > 0) {
1225 Item i = items.get(0);
1226 items.remove(i);
1227 if (i.isEnclosed()) {
1228 Polygon p = i.getEnclosedShape();
1229 if (p.contains(position.x, position.y)) {
1230 used.add(i);
1231 items.removeAll(i.getEnclosingDots());
1232 }
1233 }
1234 }
1235
1236 if (used.size() == 0)
1237 return null;
1238
1239 // if there is only one possibility, return it
1240 if (used.size() == 1) {
1241 return used.get(0).getEnclosingDots();
1242 // otherwise, determine which polygon is closest to the cursor
1243 } else {
1244 Collections.sort(used, new Comparator<Item>() {
1245 public int compare(Item d1, Item d2) {
1246 Polygon p1 = d1.getEnclosedShape();
1247 Polygon p2 = d2.getEnclosedShape();
1248
1249 int closest = Integer.MAX_VALUE;
1250 int close2 = Integer.MAX_VALUE;
1251
1252 int mouseX = DisplayIO.getMouseX();
1253 int mouseY = FrameMouseActions.getY();
1254
1255 for (int i = 0; i < p1.npoints; i++) {
1256 int diff = Math.abs(p1.xpoints[i] - mouseX)
1257 + Math.abs(p1.ypoints[i] - mouseY);
1258 int diff2 = Integer.MAX_VALUE;
1259
1260 if (i < p2.npoints)
1261 diff2 = Math.abs(p2.xpoints[i] - mouseX)
1262 + Math.abs(p2.ypoints[i] - mouseY);
1263
1264 if (diff < Math.abs(closest)) {
1265 close2 = closest;
1266 closest = diff;
1267 } else if (diff < Math.abs(close2))
1268 close2 = diff;
1269
1270 if (diff2 < Math.abs(closest)) {
1271 close2 = closest;
1272 closest = -diff2;
1273 } else if (diff2 < Math.abs(close2))
1274 close2 = diff2;
1275 }
1276
1277 if (closest > 0 && close2 > 0)
1278 return -10;
1279
1280 if (closest < 0 && close2 < 0)
1281 return 10;
1282
1283 if (closest > 0)
1284 return -10;
1285
1286 return 10;
1287 }
1288
1289 });
1290
1291 return used.get(0).getEnclosingDots();
1292 }
1293 }
1294
1295 // TODO Remove this method!!
1296 // Can just getItemsWithin be used?
1297 public static Collection<Item> getItemsEnclosedBy(Frame frame, Polygon poly) {
1298 Collection<Item> contained = frame.getItemsWithin(poly);
1299
1300 Collection<Item> results = new LinkedHashSet<Item>(contained.size());
1301
1302 // check for correct permissions
1303 for (Item item : contained) {
1304 // if the item is on the frame
1305 if (item.getParent() == frame || item.getParent() == null) {
1306 // item.Permission = Permission.full;
1307 results.add(item);
1308 // otherwise, it must be on an overlay frame
1309 } else {
1310 for (Overlay overlay : frame.getOverlays()) {
1311 if (overlay.Frame == item.getParent()) {
1312 item.setPermission(overlay.permission);
1313 results.add(item);
1314 break;
1315 }
1316 }
1317 }
1318 }
1319
1320 return results;
1321 }
1322
1323 /**
1324 * Fills the given Frame with default profile tags
1325 */
1326 public static void CreateDefaultProfile(Frame profile) {
1327 Text title = profile.getTitleItem();
1328 title.setText("Profile Frame");
1329 title.setSize(50);
1330 title.setFontStyle("Bold");
1331 title.setFamily("SansSerif");
1332 title.setColor(Color.BLUE);
1333 title.setPosition(25, 50);
1334
1335 int spacing = 50;
1336 final int intialYPos = 75;
1337 int xPos = 75;
1338 int yPos = intialYPos;
1339
1340 yPos += spacing;
1341 profile.addText(xPos, yPos, "HomeFrame: " + profile.getName(), null,
1342 profile.getName());
1343 yPos += spacing;
1344 String defaultFrameName = profile.getFramesetName() + "0";
1345 profile.addText(xPos, yPos, "DefaultFrame: " + defaultFrameName, null,
1346 defaultFrameName);
1347 yPos += spacing;
1348
1349 profile.addText(xPos, yPos, "InitialWidth: "
1350 + UserSettings.InitialWidth, null);
1351 yPos += spacing;
1352
1353 profile.addText(xPos, yPos, "InitialHeight: "
1354 + UserSettings.InitialHeight, null);
1355 yPos += spacing;
1356
1357 Text t = profile.addText(xPos, yPos, "ItemTemplate", null);
1358 t.setColor(null);
1359
1360 yPos += spacing;
1361 t = profile.addText(xPos, yPos, "AnnotationTemplate", null);
1362 t.setColor(Color.gray);
1363
1364 yPos += spacing;
1365 t = profile.addText(xPos, yPos, "CommentTemplate", null);
1366 t.setColor(Color.green.darker());
1367
1368 yPos += spacing;
1369 t = profile.addText(xPos, yPos, "StatsTemplate", null);
1370 t.setColor(Color.BLACK);
1371 t.setBackgroundColor(new Color(0.9F, 0.9F, 0.9F));
1372 t.setFamily(Text.MONOSPACED_FONT);
1373 t.setSize(14);
1374
1375 xPos = 600;
1376 yPos = intialYPos;
1377
1378 // Iterate through the help folder and add links
1379 File helpDirectory = new File(FrameIO.HELP_PATH);
1380 if (helpDirectory != null) {
1381 File[] helpFramesets = helpDirectory.listFiles();
1382 if (helpFramesets != null) {
1383
1384 // Add the title for the help index
1385 Text help = profile
1386 .addText(xPos, yPos, "@Expeditee Help", null);
1387 help.setSize(25);
1388 help.setFontStyle("Bold");
1389 help.setFamily("SansSerif");
1390 help.setColor(Item.COLOR_WHEEL[3]);
1391
1392 xPos = 625;
1393
1394 for (File helpFrameset : helpFramesets) {
1395 String framesetName = helpFrameset.getName();
1396 if (!FrameIO.isValidFramesetName(framesetName))
1397 continue;
1398 Frame indexFrame = FrameIO.LoadFrame(framesetName + '1');
1399 // Look through the folder for help index pages
1400 if (indexFrame != null
1401 && ItemUtils.FindTag(indexFrame.getItems(),
1402 "@HelpIndex") != null) {
1403 yPos += spacing;
1404 t = profile.addText(xPos, yPos, '@' + indexFrame
1405 .getFramesetName(), null);
1406 t.setLink(indexFrame.getName());
1407 }
1408 }
1409 }
1410 }
1411
1412 // FrameUtils.Parse(profile);
1413 FrameIO.SaveFrame(profile);
1414 }
1415
1416 private static void checkTDFCItemWaiting(Frame currentFrame) {
1417 Item tdfcItem = FrameUtils.getTdfcItem();
1418 // if there is a TDFC Item waiting
1419 if (tdfcItem != null) {
1420 boolean change = currentFrame.hasChanged();
1421 boolean saved = currentFrame.isSaved();
1422 // Save the parent of the item if it has not been saved
1423 if (!change && !saved) {
1424 tdfcItem.setLink(null);
1425 tdfcItem.getParent().setChanged(true);
1426 FrameIO.SaveFrame(tdfcItem.getParent());
1427 FrameGraphics.Repaint();
1428 } else {
1429 SessionStats.CreatedFrame();
1430 }
1431
1432 setTdfcItem(null);
1433 }
1434 }
1435
1436 public static void setTdfcItem(Item _tdfcItem) {
1437 FrameUtils._tdfcItem = _tdfcItem;
1438 }
1439
1440 public static Item getTdfcItem() {
1441 return FrameUtils._tdfcItem;
1442 }
1443
1444 private static Item _tdfcItem = null;
1445
1446 public static void setLastEdited(Text lastEdited) {
1447 // If the lastEdited is being changed then check if its @i
1448 Frame toReparse = null;
1449 Frame toRecalculate = null;
1450 if (LastEdited != null) {
1451 if (lastEdited != LastEdited) {
1452 if (LastEdited.startsWith("@i")) {
1453 toReparse = LastEdited.getParent();
1454 } else if (LastEdited.recalculateWhenChanged()) {
1455 toRecalculate = LastEdited.getParentOrCurrentFrame();
1456 }
1457 // Update the formula if in XRay mode
1458 if (FrameGraphics.isXRayMode() && LastEdited.hasFormula()) {
1459 LastEdited.setFormula(LastEdited.getText());
1460 }
1461 }
1462 if (lastEdited != LastEdited && LastEdited.getText().length() == 0) {
1463 LastEdited.getParentOrCurrentFrame().removeItem(LastEdited);
1464 }
1465 }
1466 LastEdited = lastEdited;
1467
1468 if (!FrameGraphics.isXRayMode()) {
1469 if (toReparse != null) {
1470 Parse(toReparse, false, false);
1471 } else if (toRecalculate != null) {
1472 toRecalculate.recalculate();
1473 }
1474 }
1475 }
1476
1477 public static Text getLastEdited() {
1478 return LastEdited;
1479 }
1480
1481 public static Collection<Text> getCurrentTextItems() {
1482 Collection<Text> currentTextItems = new LinkedHashSet<Text>();
1483 Collection<Item> currentItems = getCurrentItems(null);
1484 if (currentItems != null) {
1485 for (Item i : getCurrentItems(null)) {
1486 if (i instanceof Text && !i.isLineEnd()) {
1487 currentTextItems.add((Text) i);
1488 }
1489 }
1490 }
1491 return currentTextItems;
1492 }
1493}
Note: See TracBrowser for help on using the repository browser.