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

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

change so that overlay items that get picked up get anchored back on the overlay

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