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

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

Added SearchItem and SearchStr

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