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

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