source: trunk/src/org/expeditee/gui/Frame.java@ 294

Last change on this file since 294 was 294, checked in by ra33, 16 years ago
File size: 49.1 KB
Line 
1package org.expeditee.gui;
2
3import java.awt.Color;
4import java.awt.Image;
5import java.awt.Polygon;
6import java.awt.image.ImageObserver;
7import java.awt.image.VolatileImage;
8import java.sql.Time;
9import java.util.ArrayList;
10import java.util.Collection;
11import java.util.Collections;
12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.LinkedHashSet;
15import java.util.LinkedList;
16import java.util.List;
17import java.util.Map;
18import java.util.Stack;
19
20import org.expeditee.actions.Simple;
21import org.expeditee.io.Conversion;
22import org.expeditee.io.Logger;
23import org.expeditee.items.Dot;
24import org.expeditee.items.Item;
25import org.expeditee.items.ItemAppearence;
26import org.expeditee.items.ItemParentStateChangedEvent;
27import org.expeditee.items.ItemUtils;
28import org.expeditee.items.Line;
29import org.expeditee.items.Permission;
30import org.expeditee.items.Text;
31import org.expeditee.items.XRayable;
32import org.expeditee.items.Item.HighlightMode;
33import org.expeditee.items.widgets.InteractiveWidget;
34import org.expeditee.items.widgets.WidgetCorner;
35import org.expeditee.stats.SessionStats;
36
37/**
38 * Represents a Expeditee Frame that is displayed on the screen. Also is a
39 * registered MouseListener on the Browser, and processes any MouseEvents
40 * directly.
41 *
42 * @author jdm18
43 *
44 */
45public class Frame implements ImageObserver {
46
47 private boolean _protectionChanged = false;
48
49 public boolean isReadOnly() {
50 return !_frameName.hasPermission(Permission.full)
51 && !_protectionChanged;
52 }
53
54 public static Color[] COLOR_WHEEL = { new Color(235, 235, 235),
55 new Color(225, 225, 255), new Color(195, 255, 255),
56 new Color(225, 255, 225), new Color(255, 255, 195),
57 new Color(255, 225, 225), new Color(255, 195, 255), Color.WHITE,
58 Color.GRAY, Color.DARK_GRAY, Color.BLACK, null };
59
60 // The various attributes of this Frame
61 private String _frameset = null;
62
63 private int _number = -1;
64
65 private int _version = 0;
66
67 private Permission _permission = null;
68
69 private String _owner = null;
70
71 private String _creationDate = null;
72
73 private String _modifiedUser = null;
74
75 private String _modifiedDate = null;
76
77 private String _frozenDate = null;
78
79 // Background color is clear
80 private Color _background = null;
81
82 // Foreground color is automatic by default
83 private Color _foreground = null;
84
85 public String path;
86
87 private boolean _sorted = true;
88
89 // The items contained in this Frame
90 // records whether a change has been made to this Frame (for saving
91 // purposes).
92 private boolean _change = false;
93
94 private boolean _saved = false;
95
96 // list of deleted items that can be restored
97 private Stack<Item> _undo = new Stack<Item>();
98
99 // basically just a list of smaller objects?
100 // maybe a hashtable (id -> item?)
101 // Note: Needs to be able to be iterated through (for painting)
102 private List<Item> _body = new ArrayList<Item>();
103
104 // for drawing purposes
105 private List<InteractiveWidget> _iWidgets = new ArrayList<InteractiveWidget>();
106
107 private int _lineCount = 0;
108
109 private int _itemCount = 1;
110
111 // The frameName to display on the screen
112 private Text _frameName = null;
113
114 private Map<Overlay, Frame> _overlays = new HashMap<Overlay, Frame>();
115
116 private List<Vector> _vectors = new ArrayList<Vector>();
117
118 private Image _buffer = null;
119
120 private boolean _validBuffer = true;
121
122 private Time _activeTime = new Time(0);
123
124 private Time _darkTime = new Time(0);
125
126 private Collection<Item> _overlayItems = new LinkedHashSet<Item>();
127
128 private Collection<Item> _vectorItems = new LinkedHashSet<Item>();
129
130 /**
131 * Default constructor, nothing is set.
132 */
133 public Frame() {
134 }
135
136 public Image getBuffer() {
137 return _buffer;
138 }
139
140 public void setBuffer(Image newBuffer) {
141 _buffer = newBuffer;
142 }
143
144 public boolean isBufferValid() {
145 if (_buffer == null
146 || (_buffer instanceof VolatileImage && ((VolatileImage) _buffer)
147 .contentsLost()))
148 return false;
149
150 return _validBuffer;
151 }
152
153 private void setBufferValid(boolean newValue) {
154 _validBuffer = newValue;
155 }
156
157 public int getNextItemID() {
158 return ++_itemCount;
159 }
160
161 public void updateIDs(List<Item> items) {
162 for (Item i : items)
163 if (!(i instanceof Line))
164 i.setID(getNextItemID());
165 else
166 i.setID(++_lineCount);
167 }
168
169 /**
170 *
171 * @return The interactive widgets that are currently ancored in this frame.
172 * Hence it exlcudes free-widgets. Returns a copy
173 */
174 public List<InteractiveWidget> getInteractiveWidgets() {
175 LinkedList<InteractiveWidget> clone = new LinkedList<InteractiveWidget>();
176 clone.addAll(this._iWidgets);
177 return clone;
178 }
179
180 /**
181 * Returns whether this Frame has been changed and required saving to disk.
182 *
183 * @return True if this Frame has been altered, false otherwise.
184 */
185 public boolean hasChanged() {
186 // virtual frames are never saved
187 if (_number == -1)
188 return false;
189
190 return _change;
191 }
192
193 /**
194 * Sets whether this Frame should be saved to disk.
195 *
196 * @param value
197 * False if this Frame should be saved to disk, False otherwise.
198 */
199 public void setChanged(boolean value) {
200 // System.out.println(getName() + " " + value);
201 boolean oldValue = _change;
202
203 if (oldValue == value)
204 return;
205
206 _change = value;
207
208 if (_change) {
209 notifyObservers();
210
211 setBufferValid(false);
212 _saved = false;
213 }
214 }
215
216 public void notifyObservers() {
217 // Notify the frame listeners that the frame has changed
218 for (FrameObserver fl : _observers) {
219 fl.update();
220 }
221 }
222
223 // indicates the frame has changed
224 public void change() {
225 setChanged(true);
226 }
227
228 /**
229 * Returns an ArrayList of all Items currently on the Frame (excludes Items
230 * attached to the cursor).
231 *
232 * @return The list of Item objects that are on this Frame.
233 */
234 public List<Item> getItems(boolean visible) {
235
236 if (!_sorted) {
237 Collections.sort(_body);
238 _sorted = true;
239 }
240
241 List<Item> items = new ArrayList<Item>();
242
243 for (Item i : _body) {
244 if (i == null)
245 continue;
246 if (i.isVisible() || (!visible && !i.isDeleted())) {
247 items.add(i);
248 }
249 }
250
251 return items;
252 }
253
254 public List<Item> getItems() {
255 return getItems(false);
256 }
257
258 /**
259 * @param i
260 * Item to check if contained in this frame
261 * @return True if this frame contains i.
262 */
263 public boolean containsItem(Item i) {
264 if (i == null)
265 throw new NullPointerException("i");
266 return _body.contains(i);
267 }
268
269 /**
270 * Returns a list of all the non annotation text items on the frame which
271 * are not the title or frame name or special annotation items.
272 *
273 * @param includeAnnotations
274 * true if annotation items without special meaning should be
275 * included
276 * @param includeLineEnds
277 * true if text on the end of lines should be included in the
278 * list
279 * @return the list of body text items.
280 */
281 public List<Text> getBodyTextItems(boolean includeAnnotations) {
282 List<Text> bodyTextItems = new ArrayList<Text>();
283 for (Item i : getItems(true)) {
284 // only add up normal body text items
285 if ((i instanceof Text)
286 && ((includeAnnotations && !((Text) i)
287 .isSpecialAnnotation()) || !i.isAnnotation())
288 && !i.isLineEnd()) {
289 bodyTextItems.add((Text) i);
290 }
291 }
292 bodyTextItems.remove(getTitleItem());
293
294 return bodyTextItems;
295 }
296
297 public Collection<Item> getNonAnnotationItems(boolean removeTitle) {
298 Collection<Item> items = new ArrayList<Item>();
299 for (Item i : getItems(true)) {
300 // only add up normal body text items
301 if (!i.isAnnotation()) {
302 items.add(i);
303 }
304 }
305 if (removeTitle) {
306 items.remove(getTitleItem());
307 }
308 return items;
309 }
310
311 /**
312 * Gets the last item on the frame that is a non annotation item but is also
313 * text.
314 *
315 * @return the last non annotation text item.
316 */
317 public Item getLastNonAnnotationTextItem() {
318 List<Item> items = getItems();
319
320 // find the last non-annotation text item
321 for (int i = (items.size() - 1); i >= 0; i--) {
322 Item it = items.get(i);
323
324 if (it instanceof Text && !it.isAnnotation()) {
325 return (Item) it;
326 }
327 }
328 return null;
329 }
330
331 /**
332 * Iterates through the list of items on the frame, and returns one with the
333 * given id if one exists, otherwise returns null.
334 *
335 * @param id
336 * The id to search for in the list of items
337 * @return The item on this frame with the given ID, or null if one is not
338 * found.
339 */
340 public Item getItemWithID(int id) {
341 for (Item i : _body)
342 if (i.getID() == id)
343 return i;
344
345 return null;
346 }
347
348 /**
349 * Sets this Frame's Title which is displayed in the top left corner.
350 *
351 * @param title
352 * The title to assign to this Frame
353 */
354 public void setTitle(String title) {
355 if (title == null || title.equals(""))
356 return;
357
358 boolean oldchange = _change;
359
360 // remove any numbering this title has
361 title = title.replaceAll("^\\d*[.] *", "");
362 Text frameTitle = getTitleItem();
363
364 if (frameTitle == null) {
365 if (UserSettings.TitleTemplate == null) {
366 frameTitle = new Text(getNextItemID(), title);
367 frameTitle.resetTitlePosition();
368 } else {
369 frameTitle = UserSettings.TitleTemplate.copy();
370 frameTitle.setID(this.getNextItemID());
371 frameTitle.setText(title);
372 }
373 addItem(frameTitle);
374 } else {
375 // If it begins with a tag remove it
376
377 // Remove the @ symbol if it is there
378 // title = ItemUtils.StripTagSymbol(title);
379 frameTitle.setText(title);
380 // If the @ symbol is followed by numbering or a bullet remove that
381 // too
382 String autoBulletText = FrameKeyboardActions.getAutoBullet(title);
383 if (autoBulletText.length() > 0)
384 frameTitle.stripFirstWord();
385 }
386 // TODO Widgets... check this out
387 // Brook: Cannot figure what is going on above... widget annot titles
388 // should be stripped always
389 if (ItemUtils.startsWithTag(frameTitle, ItemUtils
390 .GetTag(ItemUtils.TAG_IWIDGET))) {
391 frameTitle.stripFirstWord();
392 }
393
394 FrameUtils.Parse(this);
395
396 // do not save if this is the only change
397 setChanged(oldchange);
398 }
399
400 public Text getTitleItem() {
401 List<Item> items = getItems();
402 for (Item i : items) {
403 if (i instanceof Text && i.getX() < UserSettings.TitlePosition
404 && i.getY() < UserSettings.TitlePosition)
405 return (Text) i;
406 }
407
408 return null;
409 }
410
411 public String getTitle() {
412 Text title = getTitleItem();
413 if (title == null)
414 return getName();
415
416 return title.getFirstLine();
417 }
418
419 public Item getNameItem() {
420 return _frameName;
421 }
422
423 public Text getItemTemplate() {
424 return getTemplate(UserSettings.ItemTemplate,
425 ItemUtils.TAG_ITEM_TEMPLATE);
426 }
427
428 public Text getAnnotationTemplate() {
429 Text t = getTemplate(UserSettings.AnnotationTemplate,
430 ItemUtils.TAG_ANNOTATION_TEMPLATE);
431
432 if (t == null) {
433 t = getItemTemplate();
434 }
435
436 return t;
437 }
438
439 public Text getStatTemplate() {
440 SessionStats.CreatedText();
441 Text t = getTemplate(UserSettings.StatTemplate,
442 ItemUtils.TAG_STAT_TEMPLATE);
443
444 if (t == null) {
445 t = getItemTemplate();
446 }
447
448 return t;
449 }
450
451 public Item getStatsTextItem(String itemText) {
452 return getTextItem(itemText, getStatTemplate());
453 }
454
455 public Item getTextItem(String itemText) {
456 return getTextItem(itemText, getItemTemplate());
457 }
458
459 private Item getTextItem(String itemText, Text template) {
460 Text t = template;
461 // We dont want the stats to wrap at all
462 // t.setMaxWidth(Integer.MAX_VALUE);
463 t.setPosition(DisplayIO.getMouseX(), FrameMouseActions.getY());
464 // The next line is needed to make sure the item is removed from the
465 // frame when picked up
466 t.setParent(this);
467 t.setText(itemText);
468 return t;
469 }
470
471 public Text getCodeCommentTemplate() {
472 Text t = getTemplate(UserSettings.CodeCommentTemplate,
473 ItemUtils.TAG_CODE_COMMENT_TEMPLATE);
474
475 if (t == null) {
476 t = getItemTemplate();
477 }
478
479 return t;
480 }
481
482 /**
483 * Returns any items on this frame that are within the given Shape. Also
484 * returns any Items on overlay frames that are within the Shape.
485 *
486 * @param shape
487 * The Shape to search for Items in
488 * @return All Items on this Frame or overlayed Frames for which
489 * Item.intersects(shape) return true.
490 */
491 public Collection<Item> getItemsWithin(Polygon poly) {
492 Collection<Item> results = new LinkedHashSet<Item>();
493 for (Item i : getVisibleItems()) {
494 if (i.intersects(poly)) {
495 if (i instanceof XRayable) {
496 results.addAll(i.getConnected());
497 // Dont add circle centers
498 // TODO change this to be isCircle center
499 } else if (!i.hasEnclosures()) {
500 results.add(i);
501 }
502 }
503 }
504
505 for (Overlay o : _overlays.keySet())
506 results.addAll(o.Frame.getItemsWithin(poly));
507
508 for (Item i : getVectorItems()) {
509 if (i.intersects(poly)) {
510 // This assumes a results is a set
511 results.add(i.getEditTarget());
512 }
513 }
514
515 return results;
516 }
517
518 /**
519 * Sets the name of this Frame to the given String, to be displayed in the
520 * upper right corner.
521 *
522 * @param name
523 * The name to use for this Frame.
524 */
525 public void setFrameset(String name) {
526 _frameset = name;
527 }
528
529 public void setName(String framename) {
530 int num = Conversion.getFrameNumber(framename);
531 String frameset = Conversion.getFramesetName(framename, false);
532
533 setName(frameset, num);
534 }
535
536 /**
537 * Sets the frame number of this Frame to the given integer
538 *
539 * @param number
540 * The number to set as the frame number
541 */
542 public void setFrameNumber(int number) {
543 assert (number >= 0);
544
545 if (_number == number)
546 return;
547
548 _number = number;
549 boolean oldchange = _change;
550
551 int id;
552
553 if (_frameName != null) {
554 id = _frameName.getID();
555 } else {
556 id = -1 * getNextItemID();
557 }
558 _frameName = new Text(id);
559 _frameName.setParent(this);
560 _frameName.setText(getFramesetName() + _number);
561 _frameName.resetFrameNamePosition();
562 setChanged(oldchange);
563 }
564
565 /**
566 * Returns the number of this Frame.
567 *
568 * @return The Frame number of this Frame or -1 if it is not set.
569 */
570 public int getNumber() {
571 return _number;
572 }
573
574 /**
575 * Increments the version of this Frame to the given String.
576 *
577 * @param version
578 * The version to use for this Frame.
579 */
580 public void setVersion(int version) {
581 _version = version;
582 }
583
584 /**
585 * Sets the protection of this Frame to the given String.
586 *
587 * @param protection
588 * The protection to use for this Frame.
589 */
590 public void setPermission(Permission permission) {
591 if (_permission != null && _permission.equals(permission))
592 _protectionChanged = true;
593
594 _permission = permission;
595
596 if (_body.size() > 0)
597 refreshItemPermissions(permission);
598 }
599
600 /**
601 * Sets the owner of this Frame to the given String.
602 *
603 * @param owner
604 * The owner to use for this Frame.
605 */
606 public void setOwner(String owner) {
607 _owner = owner;
608 }
609
610 /**
611 * Sets the created date of this Frame to the given String.
612 *
613 * @param date
614 * The date to use for this Frame.
615 */
616 public void setDateCreated(String date) {
617 _creationDate = date;
618 _modifiedDate = date;
619 for (Item i : _body) {
620 i.setDateCreated(date);
621 }
622 }
623
624 public void resetDateCreated() {
625 setDateCreated(Logger.EasyDateFormat("ddMMMyyyy:HHmm"));
626 setActiveTime(new Time(0));
627 setDarkTime(new Time(0));
628 setVersion(0);
629 }
630
631 /**
632 * Sets the last modifying user of this Frame to the given String.
633 *
634 * @param user
635 * The user to set as the last modifying user.
636 */
637 public void setLastModifyUser(String user) {
638 _modifiedUser = user;
639 }
640
641 /**
642 * Sets the last modified date of this Frame to the given String.
643 *
644 * @param date
645 * The date to set as the last modified date.
646 */
647 public void setLastModifyDate(String date) {
648 _modifiedDate = date;
649 }
650
651 /**
652 * Sets the last frozen date of this Frame to the given String.
653 *
654 * @param date
655 * The date to set as the last frozen date.
656 */
657 public void setFrozenDate(String date) {
658 _frozenDate = date;
659 }
660
661 public void setResort(boolean value) {
662 _sorted = !value;
663 }
664
665 /**
666 * Adds the given Item to the body of this Frame.
667 *
668 * @param item
669 * The Item to add to this Frame.
670 */
671 public void addItem(Item item) {
672 addItem(item, true);
673 }
674
675 public void addItem(Item item, boolean recalculate) {
676 if (item == null || item.equals(_frameName) || _body.contains(item))
677 return;
678
679 // When an annotation item is anchored the annotation list must be
680 // refreshed
681 if (item.isAnnotation()) {
682 clearAnnotations();
683 }
684
685 if (item instanceof Line)
686 _lineCount++;
687
688 _itemCount = Math.max(_itemCount, item.getID());
689
690 _body.add(item);
691 item.setParent(this);
692 item.setFloating(false); // esnure that it is anchored
693
694 item.invalidateCommonTrait(ItemAppearence.Added);
695
696 // If the item is a line end and has constraints with items already
697 // on the frame then make sure the constraints hold
698 if (item.isLineEnd()) {
699 item.setPosition(item.getPosition());
700 }
701
702 _sorted = false;
703
704 // item.setMaxWidth(FrameGraphics.getMaxFrameSize().width);
705 // add widget items to the list of widgets
706 if (item instanceof WidgetCorner) {
707 InteractiveWidget iw = ((WidgetCorner) item).getWidgetSource();
708 if (!this._iWidgets.contains(iw)) { // A set would have been
709 if (FrameMouseActions.isControlDown())
710 _iWidgets.add(iw);
711 else
712 _iWidgets.add(0, iw);
713 }
714 }
715
716 item.onParentStateChanged(new ItemParentStateChangedEvent(this,
717 ItemParentStateChangedEvent.EVENT_TYPE_ADDED));
718 if (recalculate && item.recalculateWhenChanged())
719 recalculate();
720
721 change();
722 }
723
724 public void refreshSize() {
725 // assert (size != null);
726 boolean bReparse = false;
727 for (Item i : getItems()) {
728 Float anchorBottom = i.getAnchorBottom();
729 Float anchorRight = i.getAnchorRight();
730 if (anchorRight != null) {
731 i.setAnchorRight(anchorRight);
732 if (i.hasVector()) {
733 bReparse = true;
734 }
735 }
736 if (anchorBottom != null) {
737 i.setAnchorBottom(anchorBottom);
738 if (i.hasVector()) {
739 bReparse = true;
740 }
741 }
742 }
743
744 // Do the anchors on the overlays
745 for (Overlay o : getOverlays()) {
746 o.Frame.refreshSize();
747 }
748
749 if (bReparse) {
750 FrameUtils.Parse(this, false);
751 }
752
753 _frameName.resetFrameNamePosition();
754 }
755
756 public void addAllItems(Collection<Item> toAdd) {
757 for (Item i : toAdd) {
758 // If an annotation is being deleted clear the annotation list
759 if (i.isAnnotation())
760 i.getParentOrCurrentFrame().clearAnnotations();
761 // TODO Improve efficiency when addAll is called
762 addItem(i);
763 }
764 }
765
766 public void removeAllItems(Collection<Item> toRemove) {
767 for (Item i : toRemove) {
768 // If an annotation is being deleted clear the annotation list
769 if (i.isAnnotation())
770 i.getParentOrCurrentFrame().clearAnnotations();
771 removeItem(i);
772 }
773 }
774
775 public void removeItem(Item item) {
776 removeItem(item, true);
777 }
778
779 public void removeItem(Item item, boolean recalculate) {
780 // If an annotation is being deleted clear the annotation list
781 if (item.isAnnotation())
782 item.getParentOrCurrentFrame().clearAnnotations();
783
784 if (_body.remove(item)) {
785 change();
786 // Remove widgets from the widget list
787 if (item != null) {
788 item.onParentStateChanged(new ItemParentStateChangedEvent(this,
789 ItemParentStateChangedEvent.EVENT_TYPE_REMOVED));
790 if (item instanceof WidgetCorner) {
791 _iWidgets.remove(((WidgetCorner) item).getWidgetSource());
792 }
793 item.invalidateCommonTrait(ItemAppearence.Removed);
794 }
795 // TODO Improve efficiency when removeAll is called
796 if (recalculate && item.recalculateWhenChanged())
797 recalculate();
798 }
799 }
800
801 /**
802 * Adds the given list of Items to the undo stack. This is the same as
803 * calling addToUndo() for each Item in the list.
804 *
805 * @param items
806 * The List of Items to add to the undo stack.
807 */
808 public void addAllToUndo(Collection<Item> items) {
809 if (items.size() < 1)
810 return;
811
812 String id = "" + _undo.size();
813
814 for (Item i : items) {
815 i.setTag(id);
816 _undo.push(i);
817 }
818 }
819
820 public void addToUndo(Item item) {
821 if (item == null)
822 return;
823
824 item.setTag("" + _undo.size());
825 _undo.push(item);
826 }
827
828 public void undo() {
829 Item undo = null;
830 boolean bReparse = false;
831
832 if (_undo.size() <= 0)
833 return;
834
835 undo = _undo.pop();
836
837 // if the change was to characteristics
838 if (undo.isVisible() && _body.contains(undo)) {
839 Item old = _body.get(_body.indexOf(undo));
840 _body.set(_body.indexOf(old), undo);
841 // the item was deleted
842 } else {
843 List<Item> toRestore = new LinkedList<Item>();
844 toRestore.add(undo);
845
846 // remove any connected items at the top of the stack
847 while (_undo.size() > 0) {
848 Item next = _undo.peek();
849 // if this item was connected to one already picked up, remove
850 // it from the stack
851 if (toRestore.contains(next))
852 _undo.pop();
853 // else, if this item should be restored (deleted in an enclosed
854 // set)
855 else if (next.getTag().equals(undo.getTag())) {
856 _undo.pop();
857 toRestore.add(next);
858 // otherwise, we are done
859 } else
860 break;
861 }
862
863 // if these items were deleted from a frame, add them
864 addAllItems(toRestore);
865
866 for (Item i : toRestore) {
867 bReparse |= i.hasOverlay();
868 if (i instanceof Line) {
869 Line line = (Line) i;
870 line.getStartItem().addLine(line);
871 line.getEndItem().addLine(line);
872 } else {
873 i.setOffset(0, 0);
874 }
875 }
876 }
877
878 change();
879 FrameMouseActions.getInstance().refreshHighlights();
880 if (bReparse)
881 FrameUtils.Parse(this, false);
882 FrameGraphics.Repaint();
883 ItemUtils.EnclosedCheck(_body);
884 }
885
886 /**
887 * Returns the frameset of this Frame
888 *
889 * @return The name of this Frame's frameset.
890 */
891 public String getFramesetName() {
892 return _frameset;
893 }
894
895 public String getName() {
896 return getFramesetName() + _number;
897 }
898
899 /**
900 * Returns the format version of this Frame
901 *
902 * @return The version of this Frame.
903 */
904 public int getVersion() {
905 return _version;
906 }
907
908 public Permission getPermission() {
909 return getPermission(Permission.full);
910 }
911
912 public Permission getPermission(Permission defaultPermission) {
913 if (_permission == null)
914 return defaultPermission;
915
916 return _permission;
917 }
918
919 public String getOwner() {
920 return _owner;
921 }
922
923 public String getDateCreated() {
924 return _creationDate;
925 }
926
927 public String getLastModifyUser() {
928 return _modifiedUser;
929 }
930
931 public String getLastModifyDate() {
932 return _modifiedDate;
933 }
934
935 public String getFrozenDate() {
936 return _frozenDate;
937 }
938
939 public void setBackgroundColor(Color back) {
940 _background = back;
941 change();
942
943 if (this == DisplayIO.getCurrentFrame()) {
944 FrameGraphics.refresh(false);
945 }
946 }
947
948 public Color getBackgroundColor() {
949 return _background;
950 }
951
952 public Color getPaintBackgroundColor() {
953 // If null... return white
954 if (_background == null) {
955 return Item.DEFAULT_BACKGROUND;
956 }
957
958 return _background;
959 }
960
961 public void setForegroundColor(Color front) {
962 _foreground = front;
963 change();
964 // FrameGraphics.Repaint();
965 }
966
967 public Color getForegroundColor() {
968 return _foreground;
969 }
970
971 public Color getPaintForegroundColor() {
972 final int GRAY = Color.gray.getBlue();
973 final int THRESHOLD = 10;
974
975 if (_foreground == null) {
976 Color back = getPaintBackgroundColor();
977 if (Math.abs(back.getRed() - GRAY) < THRESHOLD
978 && Math.abs(back.getBlue() - GRAY) < THRESHOLD
979 && Math.abs(back.getGreen() - GRAY) < THRESHOLD)
980 return Color.WHITE;
981
982 Color fore = new Color(
983 Math.abs(Conversion.RGB_MAX - back.getRed()), Math
984 .abs(Conversion.RGB_MAX - back.getGreen()), Math
985 .abs(Conversion.RGB_MAX - back.getBlue()));
986 return fore;
987 }
988
989 return _foreground;
990 }
991
992 public String toString() {
993 StringBuilder s = new StringBuilder();
994 s.append(String.format("Name: %s%d%n", _frameset, _number));
995 s.append(String.format("Version: %d%n", _version));
996 s.append(String.format("Permission: %s%n", _permission.toString()));
997 s.append(String.format("Owner: %s%n", _owner));
998 s.append(String.format("Date Created: %s%n", _creationDate));
999 s.append(String.format("Last Mod. User: %s%n", _modifiedUser));
1000 s.append(String.format("Last Mod. Date: %s%n", _modifiedDate));
1001 s.append(String.format("Items: %d%n", _body.size()));
1002 return s.toString();
1003 }
1004
1005 public Text getTextAbove(Text current) {
1006 Collection<Text> currentTextItems = FrameUtils.getCurrentTextItems();
1007 List<Text> toCheck = new ArrayList<Text>();
1008 if (currentTextItems.contains(current)) {
1009 toCheck.addAll(currentTextItems);
1010 } else {
1011 toCheck.addAll(getTextItems());
1012 }
1013 // Make sure the items are sorted
1014 Collections.sort(toCheck);
1015
1016 int ind = toCheck.indexOf(current);
1017 if (ind == -1)
1018 return null;
1019
1020 // loop through all items above this one, return the first match
1021 for (int i = ind - 1; i >= 0; i--) {
1022 Text check = toCheck.get(i);
1023 if (FrameUtils.inSameColumn(check, current))
1024 return check;
1025 }
1026
1027 return null;
1028 }
1029
1030 /**
1031 * Updates any Images that require it from their ImageObserver (Principally
1032 * Animated GIFs)
1033 */
1034 public boolean imageUpdate(Image img, int infoflags, int x, int y,
1035 int width, int height) {
1036 FrameGraphics.ForceRepaint();
1037
1038 if (DisplayIO.getCurrentFrame() == this)
1039 return true;
1040
1041 return false;
1042 }
1043
1044 /**
1045 * Gets the text items that are in the same column and below a specified
1046 * item. Frame title and name are excluded from the column list.
1047 *
1048 * @param from
1049 * The Item to get the column for.
1050 */
1051 public List<Text> getColumn(Item from) {
1052 // Check that this item is on the current frame
1053 if (!_body.contains(from))
1054 return null;
1055
1056 if (from == null) {
1057 from = getLastNonAnnotationTextItem();
1058 }
1059
1060 if (from == null)
1061 return null;
1062
1063 // Get the enclosedItems
1064 Collection<Text> enclosed = FrameUtils.getCurrentTextItems();
1065 List<Text> toCheck = null;
1066 if (enclosed.contains(from)) {
1067 toCheck = new ArrayList<Text>();
1068 toCheck.addAll(enclosed);
1069 } else {
1070 toCheck = getBodyTextItems(true);
1071 }
1072
1073 List<Text> column = new ArrayList<Text>();
1074 if (toCheck.size() > 0) {
1075
1076 // Make sure the items are sorted
1077 Collections.sort(toCheck);
1078
1079 // Create a list of items consisting of the item 'from' and all the
1080 // items below it which are also in the same column as it
1081 int index = toCheck.indexOf(from);
1082
1083 // If its the title index will be 0
1084 if (index < 0)
1085 index = 0;
1086
1087 for (int i = index; i < toCheck.size(); i++) {
1088 Text item = toCheck.get(i);
1089 if (FrameUtils.inSameColumn(from, item))
1090 column.add(item);
1091 }
1092 }
1093
1094 return column;
1095 }
1096
1097 /**
1098 * Adds the given Vector to the list of vector Frames being drawn with this
1099 * Frame.
1100 *
1101 * @param vector
1102 * The Vector to add
1103 *
1104 * @throws NullPointerException
1105 * If overlay is null.
1106 */
1107 protected boolean addVector(Vector toAdd) {
1108 // make sure we dont add this frame as an overlay of itself
1109 if (toAdd.Frame == this)
1110 return false;
1111 _vectors.add(toAdd);
1112 // Items must be notified that they have been added or removed from this
1113 // frame via the vector...
1114 int maxX = 0;
1115 int maxY = 0;
1116 HighlightMode mode = toAdd.Source.getHighlightMode();
1117 if (mode != HighlightMode.None)
1118 mode = HighlightMode.Connected;
1119 Color highlightColor = toAdd.Source.getHighlightColor();
1120 for (Item i : ItemUtils.CopyItems(toAdd.Frame.getVectorItems(), toAdd)) {
1121 i.onParentStateChanged(new ItemParentStateChangedEvent(this,
1122 ItemParentStateChangedEvent.EVENT_TYPE_ADDED_VIA_OVERLAY,
1123 toAdd.permission));
1124 i.setEditTarget(toAdd.Source);
1125 i.setHighlightMode(mode, highlightColor);
1126 _vectorItems.add(i);
1127 i.invalidateAll();
1128 i.invalidateFill();
1129 // Get the right most x and bottom most y pos
1130 int itemRight = i.getX() + i.getBoundsWidth();
1131 if (itemRight > maxX)
1132 maxX = itemRight;
1133 int itemBottom = i.getY() + i.getBoundsHeight();
1134 if (itemBottom > maxY)
1135 maxY = itemBottom;
1136 }
1137 toAdd.setSize(maxX, maxY);
1138 return true;
1139 }
1140
1141 public Collection<Vector> getVectors() {
1142 Collection<Vector> l = new LinkedList<Vector>();
1143 l.addAll(_vectors);
1144 return l;
1145 }
1146
1147 public Collection<Overlay> getOverlays() {
1148 return new LinkedList<Overlay>(_overlays.keySet());
1149 }
1150
1151 /**
1152 * @return All vectosr seen by this frame (including its vector's vectors).
1153 */
1154 public List<Vector> getVectorsDeep() {
1155 List<Vector> l = new LinkedList<Vector>();
1156 getVectorsDeep(l, this, new LinkedList<Frame>());
1157 return l;
1158 }
1159
1160 private boolean getVectorsDeep(List<Vector> vectors, Frame vector,
1161 List<Frame> seenVectors) {
1162
1163 if (seenVectors.contains(vector))
1164 return false;
1165
1166 seenVectors.add(vector);
1167
1168 for (Vector o : vector.getVectors()) {
1169 if (getVectorsDeep(vectors, o.Frame, seenVectors)) {
1170 vectors.add(o);
1171 }
1172 }
1173
1174 return true;
1175 }
1176
1177 // private boolean getOverlaysDeep(List<Overlay> overlays, Frame overlay,
1178 // List<Frame> seenOverlays) {
1179 //
1180 // if (seenOverlays.contains(overlay))
1181 // return false;
1182 //
1183 // seenOverlays.add(overlay);
1184 //
1185 // for (Overlay o : overlay.getOverlays()) {
1186 // if (getOverlaysDeep(overlays, o.Frame, seenOverlays)) {
1187 // overlays.add(o);
1188 // }
1189 // }
1190 //
1191 // return true;
1192 // }
1193
1194 /**
1195 * Gets the overlay on this frame which owns the given item.
1196 *
1197 * @param item
1198 * The item - must not be null.
1199 * @return The overlay that contains the itm. Null if no overlay owns the
1200 * item.
1201 */
1202 public Overlay getOverlayOwner(Item item) {
1203 if (item == null)
1204 throw new NullPointerException("item");
1205
1206 for (Overlay l : getOverlays()) {
1207 if (item.getParent() == l.Frame)
1208 return l;
1209 }
1210
1211 // TODO return the correct vector... not just the first vector matching
1212 // the vectorFrame
1213 for (Vector v : getVectors()) {
1214 if (item.getParent() == v.Frame)
1215 return v;
1216 }
1217
1218 return null;
1219 }
1220
1221 public void clearVectors() {
1222 _vectors.clear();
1223
1224 for (Item i : _vectorItems) { // TODO: Rethink where this should live
1225 i.invalidateAll();
1226 i.invalidateFill();
1227 }
1228 _vectorItems.clear();
1229
1230 }
1231
1232 protected boolean removeVector(Vector toRemove) {
1233 if (!_vectors.remove(toRemove))
1234 return false;
1235 for (Item i : toRemove.Frame.getVectorItems()) {
1236 i.invalidateAll();
1237 i.invalidateFill();
1238 _vectorItems.remove(i);
1239 i.onParentStateChanged(new ItemParentStateChangedEvent(this,
1240 ItemParentStateChangedEvent.EVENT_TYPE_REMOVED_VIA_OVERLAY,
1241 toRemove.permission));
1242
1243 }
1244 return true;
1245 }
1246
1247 public void clearOverlays() {
1248 for (Overlay o : _overlays.keySet()) {
1249 for (Item i : o.Frame.getItems()) {
1250 i
1251 .onParentStateChanged(new ItemParentStateChangedEvent(
1252 this,
1253 ItemParentStateChangedEvent.EVENT_TYPE_REMOVED_VIA_OVERLAY,
1254 o.permission));
1255 }
1256 }
1257 _overlayItems.clear();
1258 _overlays.clear();
1259 assert (_overlays.isEmpty());
1260 }
1261
1262 protected boolean removeOverlay(Frame f) {
1263 for (Overlay o : _overlays.keySet()) {
1264 if (o.Frame == f) {
1265 _overlays.remove(o);
1266 for (Item i : f.getItems()) {
1267 _overlayItems.remove(i);
1268 i
1269 .onParentStateChanged(new ItemParentStateChangedEvent(
1270 this,
1271 ItemParentStateChangedEvent.EVENT_TYPE_REMOVED_VIA_OVERLAY,
1272 o.permission));
1273 }
1274 return true;
1275 }
1276 }
1277 return false;
1278 }
1279
1280 public void addAllVectors(List<Vector> vectors) {
1281 for (Vector v : vectors) {
1282 addVector(v);
1283 }
1284 }
1285
1286 public void addAllOverlays(Collection<Overlay> overlays) {
1287 for (Overlay o : overlays) {
1288 addOverlay(o);
1289 }
1290 }
1291
1292 protected boolean addOverlay(Overlay toAdd) {
1293 // make sure we dont add this frame as an overlay of itself
1294 if (toAdd.Frame == this)
1295 return false;
1296 // Dont add the overlay if there is already one for this frame
1297 if (_overlays.values().contains(toAdd.Frame))
1298 return false;
1299 // Add the overlay to the map of overlays on this frame
1300 _overlays.put(toAdd, toAdd.Frame);
1301 // Add all the overlays from the overlay frame to this frame
1302 for (Overlay o : toAdd.Frame.getOverlays())
1303 addOverlay(o);
1304
1305 // Now add the items for this overlay
1306 Permission permission = Permission.min(toAdd.Frame.getPermission(),
1307 toAdd.permission);
1308
1309 // Items must be notified that they have been added or removed from this
1310 // frame via the overlay...
1311 for (Item i : toAdd.Frame.getVisibleItems()) {
1312 i.onParentStateChanged(new ItemParentStateChangedEvent(this,
1313 ItemParentStateChangedEvent.EVENT_TYPE_ADDED_VIA_OVERLAY,
1314 permission));
1315 // i.setPermission(permission);
1316 _overlayItems.add(i);
1317 }
1318 return true;
1319 }
1320
1321 @Override
1322 public boolean equals(Object o) {
1323 if (o instanceof String) {
1324 return (String.CASE_INSENSITIVE_ORDER
1325 .compare((String) o, getName()) == 0);
1326 }
1327
1328 if (o instanceof Frame) {
1329 return getName().equals(((Frame) o).getName());
1330 }
1331
1332 return super.equals(o);
1333 }
1334
1335 /**
1336 * Merge one frames contents into another.
1337 *
1338 * @param toMergeWith
1339 */
1340 private void merge(Frame toMergeWith) {
1341 if (toMergeWith == null)
1342 return;
1343
1344 List<Item> copies = ItemUtils.CopyItems(toMergeWith.getItems());
1345 copies.remove(toMergeWith.getNameItem());
1346
1347 for (Item i : copies) {
1348 if (i.getID() >= 0) {
1349 i.setID(this.getNextItemID());
1350 addItem(i);
1351 }
1352 }
1353 }
1354
1355 /**
1356 * This method is for merging frames or setting frame attributes via
1357 * injecting a text item into the frameName item.
1358 *
1359 * @param toMerge
1360 * @return the items that cant be merged
1361 */
1362 public List<Item> merge(List<Item> toMerge) {
1363 ArrayList<Item> remain = new ArrayList<Item>(0);
1364
1365 for (Item i : toMerge) {
1366 if (!(i instanceof Text))
1367 remain.add(i);
1368 else {
1369 if (!AttributeUtils.setAttribute(this, (Text) i)) {
1370 if (i.getLink() != null)
1371 merge(FrameIO.LoadFrame(i.getAbsoluteLink()));
1372 else if (FrameIO
1373 .isValidFrameName(((Text) i).getFirstLine())) {
1374 // If we get hear we are merging frames
1375 merge(FrameIO.LoadFrame(((Text) i).getFirstLine()));
1376 }
1377 }
1378 }
1379 }
1380
1381 return remain;
1382 }
1383
1384 /**
1385 * Removes all non-title non-annotation items from this Frame. All removed
1386 * items are added to the backup-stack.
1387 */
1388 public void clear(boolean keepAnnotations) {
1389 List<Item> newBody = new ArrayList<Item>(0);
1390 Item title = getTitleItem();
1391 if (title != null) {
1392 newBody.add(title);
1393 _body.remove(title);
1394 }
1395 if (keepAnnotations) {
1396 for (Item i : _body) {
1397 if (i.isAnnotation())
1398 newBody.add(i);
1399 }
1400 }
1401 _body.removeAll(newBody);
1402 addAllToUndo(_body);
1403 _body = newBody;
1404 change();
1405
1406 if (!keepAnnotations && _annotations != null)
1407 _annotations.clear();
1408 }
1409
1410 /**
1411 * Creates a new text item with the given text.
1412 *
1413 * @param text
1414 * @return
1415 */
1416 public Text createNewText(String text) {
1417 Text t = createBlankText(text);
1418 t.setText(text);
1419 return t;
1420 }
1421
1422 /**
1423 * Creates a new Text Item with no text. The newly created Item is a copy
1424 * the ItemTemplate if one is present, and inherits all the attributes of
1425 * the Template
1426 *
1427 * @return The newly created Text Item
1428 */
1429 public Text createBlankText(String templateType) {
1430 SessionStats.CreatedText();
1431 Text t;
1432 if (templateType.length() == 0)
1433 t = getItemTemplate().copy();
1434 else
1435 t = getItemTemplate(templateType.charAt(0));
1436
1437 // reset attributes
1438 t.setID(getNextItemID());
1439 t.setPosition(DisplayIO.getMouseX(), FrameMouseActions.getY());
1440 t.setText("");
1441 t.setParent(this);
1442
1443 // Set the width if the template doesnt have a width
1444 // Make it the width of the page
1445 // t.setMaxWidth(FrameGraphics.getMaxFrameSize().width);
1446 // if (t.getWidth() <= 0) {
1447 t.setRightMargin(FrameGraphics.getMaxFrameSize().width);
1448 // }
1449 addItem(t);
1450 return t;
1451 }
1452
1453 public Item createDot() {
1454 Item dot = new Dot(DisplayIO.getMouseX(), FrameMouseActions.getY(),
1455 getNextItemID());
1456
1457 Item template = getTemplate(UserSettings.DotTemplate,
1458 ItemUtils.TAG_DOT_TEMPLATE);
1459 float thickness = template.getThickness();
1460 if (thickness > 0)
1461 dot.setThickness(template.getThickness());
1462 if (template.getLinePattern() != null)
1463 dot.setLinePattern(template.getLinePattern());
1464 dot.setColor(template.getColor());
1465 dot.setFillColor(template.getFillColor());
1466 // reset attributes
1467 dot.setParent(this);
1468 return dot;
1469 }
1470
1471 private Text getTemplate(Text defaultTemplate, int templateTag) {
1472 Text t = null;
1473
1474 // check for an updated template...
1475 for (Item i : this.getItems()) {
1476 if (ItemUtils.startsWithTag(i, templateTag)) {
1477 t = (Text) i;
1478 break;
1479 }
1480 }
1481
1482 if (t == null) {
1483 if (defaultTemplate == null) {
1484 return null;
1485 }
1486 t = defaultTemplate;
1487 }
1488
1489 // If the item is linked apply any attribute pairs on the child frame
1490 String link = t.getAbsoluteLink();
1491 // need to get link first because copy doesnt copy the link
1492 t = t.copy();
1493 if (link != null) {
1494 t.setLink(null);
1495 Frame childFrame = FrameIO.LoadFrame(link);
1496 if (childFrame != null) {
1497 // read in attribute value pairs
1498 for (Text attribute : childFrame.getBodyTextItems(false)) {
1499 AttributeUtils.setAttribute(t, attribute);
1500 }
1501 }
1502 }
1503 return t;
1504 }
1505
1506 private Text getItemTemplate(char firstChar) {
1507 switch (firstChar) {
1508 case '@':
1509 return getAnnotationTemplate();
1510 case '/':
1511 case '#':
1512 return getCodeCommentTemplate();
1513 default:
1514 return getItemTemplate();
1515 }
1516 }
1517
1518 public Text createNewText() {
1519 return createNewText("");
1520 }
1521
1522 public Text addText(int x, int y, String text, String action) {
1523 Text t = createNewText(text);
1524 t.setPosition(x, y);
1525 t.addAction(action);
1526 return t;
1527 }
1528
1529 public Item addText(int x, int y, String text, String action, String link) {
1530 Item t = addText(x, y, text, action);
1531 t.setLink(link);
1532 return t;
1533 }
1534
1535 public Item addDot(int x, int y) {
1536 Item d = new Dot(x, y, getNextItemID());
1537 addItem(d);
1538 return d;
1539 }
1540
1541 public boolean isSaved() {
1542 return _saved;
1543 }
1544
1545 public void setSaved() {
1546 // System.out.println(getName() + " saved");
1547 _saved = true;
1548 _change = false;
1549 }
1550
1551 public static boolean rubberbandingLine() {
1552 return FreeItems.getInstance().size() == 2
1553 && (FreeItems.getInstance().get(0) instanceof Line || FreeItems
1554 .getInstance().get(1) instanceof Line);
1555 }
1556
1557 /**
1558 * Tests if an item is a non title, non frame name, non special annotation
1559 * text item.
1560 *
1561 * @param it
1562 * the item to be tested
1563 * @return true if the item is a normal text item
1564 */
1565 public boolean isNormalTextItem(Item it) {
1566 if (it instanceof Text && it != getTitleItem() && it != _frameName
1567 && !((Text) it).isSpecialAnnotation()) {
1568 return true;
1569 }
1570
1571 return false;
1572 }
1573
1574 /**
1575 * Moves the mouse to the end of the text item with a specified index.
1576 *
1577 * @param index
1578 */
1579 public boolean moveMouseToTextItem(int index) {
1580 List<Item> items = getItems();
1581 int itemsFound = 0;
1582 for (int i = 0; i < items.size(); i++) {
1583 Item it = items.get(i);
1584 if (isNormalTextItem(it))
1585 itemsFound++;
1586 if (itemsFound > index) {
1587 DisplayIO.setCursorPosition(((Text) it)
1588 .getParagraphEndPosition().x, it.getY());
1589 DisplayIO.resetCursorOffset();
1590 FrameGraphics.Repaint();
1591 return true;
1592 }
1593 }
1594
1595 return false;
1596 }
1597
1598 /*
1599 * public boolean moveMouseToNextTextItem(int index) { List<Item> items =
1600 * getItems(); int itemsFound = 0; for (int i = 0; i < items.size(); i++) {
1601 * Item it = items.get(i); if ( isNormalTextItem(it)) itemsFound++; if
1602 * (itemsFound > index) {
1603 * DisplayIO.setCursorPosition(((Text)it).getEndParagraphPosition().x,
1604 * it.getY()); DisplayIO.resetCursorOffset(); FrameGraphics.Repaint();
1605 * return true; } }
1606 *
1607 * return false; }
1608 */
1609
1610 /**
1611 * Searches for an annotation item called start to be used as the default
1612 * cursor location when TDFC occurs.
1613 */
1614 public boolean moveMouseToDefaultLocation() {
1615 List<Item> items = getItems();
1616
1617 for (Item it : items) {
1618 if (it instanceof Text) {
1619 Text t = (Text) it;
1620 if (t.getText().toLowerCase().startsWith("@start")
1621 || t.getText().toLowerCase().equals("@start:")) {
1622 // Used to allow users the option of putting an initial
1623 // bullet after the @start
1624 // This was replaced by width
1625 // t.stripFirstWord();
1626 t.setText("");
1627
1628 if (t.getText().equals(""))
1629 DisplayIO.getCurrentFrame().removeItem(t);
1630 if (!FreeItems.itemsAttachedToCursor()) {
1631 DisplayIO.setCursorPosition(((Text) it)
1632 .getParagraphEndPosition());
1633 DisplayIO.resetCursorOffset();
1634 }
1635 FrameGraphics.Repaint();
1636 return true;
1637 }
1638 }
1639 }
1640
1641 return false;
1642 }
1643
1644 /**
1645 * Gets the file name that actions should use to export files created by
1646 * running actions from this frame.
1647 *
1648 * @return the fileName if the frame contains an '@file' tag. Returns the
1649 * name of the frame if the tag isnt on the frame.
1650 */
1651 public String getExportFileName() {
1652 return getAnnotationValue("file");
1653 }
1654
1655 public void toggleBackgroundColor() {
1656 setBackgroundColor(ColorUtils.getNextColor(_background,
1657 Frame.COLOR_WHEEL, null));
1658 }
1659
1660 public void setName(String frameset, int i) {
1661 setFrameset(frameset);
1662 setFrameNumber(i);
1663 }
1664
1665 /**
1666 * Sets the item permissions to match the protection for the frame.
1667 *
1668 */
1669 public void refreshItemPermissions(Permission maxPermission) {
1670 Permission permission = Permission.min(maxPermission, getPermission());
1671 _frameName.setPermission(permission);
1672
1673 switch (permission) {
1674 case none:
1675 _frameName.setBackgroundColor(new Color(255, 220, 220));
1676 break;
1677 case followLinks:
1678 _frameName.setBackgroundColor(new Color(255, 230, 135));
1679 break;
1680 case copy:
1681 _frameName.setBackgroundColor(new Color(255, 255, 155));
1682 break;
1683 case createFrames:
1684 _frameName.setBackgroundColor(new Color(220, 255, 220));
1685 break;
1686 case full:
1687 _frameName.setBackgroundColor(null);
1688 break;
1689 default:
1690 assert (false);
1691 break;
1692 }
1693
1694 for (Overlay o : getOverlays())
1695 o.Frame.refreshItemPermissions(o.permission);
1696
1697 // Only update the permissions if we have to
1698 if (_body.size() > 0 && permission.equals(_body.get(0)._permission))
1699 return;
1700
1701 for (Item i : _body) {
1702 i.setPermission(permission);
1703 }
1704
1705 }
1706
1707 public boolean isTestFrame() {
1708 Text title = getTitleItem();
1709 if (title == null)
1710 return false;
1711 String action = title.getFirstAction();
1712 if (action == null)
1713 return false;
1714 action = action.toLowerCase();
1715 return action.startsWith(Simple.RUN_FRAME_ACTION)
1716 || action.startsWith(Simple.DEBUG_FRAME_ACTION);
1717 }
1718
1719 public void setActiveTime(String activeTime) {
1720 try {
1721 _activeTime = new Time(Time.valueOf(activeTime).getTime() + 12 * 60
1722 * 60 * 1000);
1723 } catch (Exception e) {
1724 _activeTime = new Time(0);
1725 }
1726 }
1727
1728 public void setActiveTime(Time activeTime) {
1729 _activeTime = activeTime;
1730 }
1731
1732 public void setDarkTime(Time darkTime) {
1733 _darkTime = darkTime;
1734 }
1735
1736 public void setDarkTime(String darkTime) {
1737 try {
1738 _darkTime = new Time(Time.valueOf(darkTime).getTime() + 12 * 60
1739 * 60 * 1000);
1740 } catch (Exception e) {
1741 _darkTime = new Time(0);
1742 }
1743 }
1744
1745 /**
1746 * Returns null if their is no backup frame or if it is invalid.
1747 *
1748 * @return the backup frame for this frame
1749 */
1750 public Frame getBackupFrame() {
1751 Text backupTag = _annotations.get("old");
1752 if (backupTag == null)
1753 return null;
1754 // TODO want another way to deal with updating of annotations items
1755 // without F12 refresh
1756 // Reparse the frame if annotation item has been modified
1757 String[] processedText = backupTag.getProcessedText();
1758 if (processedText == null) {
1759 // Reparse the frame if this item has not yet been parsed
1760 FrameUtils.Parse(this);
1761 return getBackupFrame();
1762 }
1763 // Now return the name of the backed up frame
1764 String link = backupTag.getAbsoluteLink();
1765 if (link == null || link.equalsIgnoreCase(getName()))
1766 return null;
1767
1768 Frame backup = FrameIO.LoadFrame(link);
1769 return backup;
1770 }
1771
1772 public Time getDarkTime() {
1773 return _darkTime;
1774 }
1775
1776 public Time getActiveTime() {
1777 return _activeTime;
1778 }
1779
1780 /**
1781 * Gets the number of backed up versions of this frame are saved plus 1 for
1782 * this frame.
1783 *
1784 * @return the number of frames in the backed up comet
1785 */
1786 public int getCometLength() {
1787 Frame backup = getBackupFrame();
1788 return 1 + (backup == null ? 0 : backup.getCometLength());
1789 }
1790
1791 public void addAnnotation(Text item) {
1792 if (_annotations == null) {
1793 _annotations = new HashMap<String, Text>();
1794 }
1795 // Check if this item has already been processed
1796 String[] tokens = item.getProcessedText();
1797 if (tokens != null) {
1798 if (tokens.length > 0) {
1799 _annotations.put(tokens[0], item);
1800 }
1801 return;
1802 }
1803
1804 String text = item.getText().trim();
1805 assert (text.charAt(0) == '@');
1806 // Ignore annotations with spaces after the tag symbol
1807 if (text.length() < 2 || !Character.isLetter(text.charAt(1))) {
1808 item.setProcessedText(new String[0]);
1809 return;
1810 }
1811 // The separator char must come before the first non letter otherwise we
1812 // ignore the annotation item
1813 for (int i = 2; i < text.length(); i++) {
1814 char ch = text.charAt(i);
1815 if (!Character.isLetterOrDigit(ch)) {
1816 // Must have an attribute value pair
1817 if (ch == AttributeValuePair.SEPARATOR_CHAR) {
1818 // Get the attribute
1819 String attribute = text.substring(1, i).toLowerCase();
1820 String value = "";
1821 if (text.length() > 1 + i) {
1822 value = text.substring(i + 1).trim();
1823 }
1824 item.setProcessedText(new String[] { attribute, value });
1825 _annotations.put(attribute, item);
1826 return;
1827 } else {
1828 item.setProcessedText(new String[0]);
1829 return;
1830 }
1831 }
1832 }
1833 // If it was nothing but letters and digits save the tag
1834 String lowerCaseText = text.substring(1).toLowerCase();
1835 item.setProcessedText(new String[] { lowerCaseText });
1836 _annotations.put(lowerCaseText, item);
1837 }
1838
1839 public boolean hasAnnotation(String annotation) {
1840 if (_annotations == null)
1841 refreshAnnotationList();
1842 return _annotations.containsKey(annotation.toLowerCase());
1843 }
1844
1845 /**
1846 * Returns the annotation value in full case.
1847 *
1848 * @param annotation
1849 * the annotation to retrieve the value of.
1850 * @return the annotation item value in full case or null if the annotation
1851 * is not on the frame or has no value.
1852 */
1853 public String getAnnotationValue(String annotation) {
1854 if (_annotations == null)
1855 refreshAnnotationList();
1856
1857 Text text = _annotations.get(annotation.toLowerCase());
1858 if (text == null)
1859 return null;
1860
1861 String[] tokens = text.getProcessedText();
1862 if (tokens != null && tokens.length > 1)
1863 return tokens[1];
1864 return null;
1865 }
1866
1867 Map<String, Text> _annotations = null;
1868
1869 private Collection<FrameObserver> _observers = new HashSet<FrameObserver>();
1870
1871 public void clearAnnotations() {
1872 _annotations = null;
1873 }
1874
1875 public List<Item> getVisibleItems() {
1876 return getItems(true);
1877 }
1878
1879 private void refreshAnnotationList() {
1880 if (_annotations == null)
1881 _annotations = new HashMap<String, Text>();
1882 else
1883 _annotations.clear();
1884 for (Text text : getTextItems()) {
1885 if (text.isAnnotation()) {
1886 addAnnotation(text);
1887 }
1888 }
1889 }
1890
1891 public Collection<Text> getAnnotationItems() {
1892 if (_annotations == null) {
1893 refreshAnnotationList();
1894 }
1895 return _annotations.values();
1896 }
1897
1898 /**
1899 * Gets a list of items to be saved to file by text file writers.
1900 *
1901 * @return the list of items to be saved to a text file
1902 */
1903 public List<Item> getItemsToSave() {
1904
1905 if (!_sorted) {
1906 Collections.sort(_body);
1907 _sorted = true;
1908 }
1909
1910 // iWidgets are handled specially since 8 items are written as one
1911 Collection<InteractiveWidget> seenWidgets = new LinkedHashSet<InteractiveWidget>();
1912
1913 List<Item> toSave = new ArrayList<Item>();
1914
1915 for (Item i : _body) {
1916 if (i == null || i.dontSave()) {
1917 continue;
1918 }
1919
1920 // Ensure only one of the WidgetCorners represent a single widget
1921 if (i instanceof WidgetCorner) {
1922 InteractiveWidget iw = ((WidgetCorner) i).getWidgetSource();
1923 if (seenWidgets.contains(iw))
1924 continue;
1925 seenWidgets.add(iw);
1926 toSave.add(iw.getSource());
1927 } else if (i instanceof XRayable) {
1928 XRayable x = (XRayable) i;
1929 toSave.addAll(x.getItemsToSave());
1930 }// Circle centers are items with attached enclosures
1931 else if (i.hasEnclosures()) {
1932 continue;
1933 } else {
1934 toSave.add(i);
1935 }
1936 }
1937
1938 for (Vector v : getVectors()) {
1939 toSave.add(v.Source);
1940 }
1941
1942 return toSave;
1943 }
1944
1945 public Collection<Item> getOverlayItems() {
1946 return _overlayItems;
1947 }
1948
1949 /**
1950 * Returns true if this frame has and overlays for the specified frame.
1951 *
1952 * @param frame
1953 * @return
1954 */
1955 public boolean hasOverlay(Frame frame) {
1956 return _overlays.containsValue(frame);
1957 }
1958
1959 public Collection<Item> getAllItems() {
1960 Collection<Item> allItems = new LinkedHashSet<Item>(_body);
1961 allItems.addAll(_overlayItems);
1962 allItems.addAll(_vectorItems);
1963 return allItems;
1964 }
1965
1966 public Collection<Item> getVectorItems() {
1967 Collection<Item> vectorItems = new LinkedHashSet<Item>(_vectorItems);
1968 vectorItems.addAll(getNonAnnotationItems(false));
1969 return vectorItems;
1970 }
1971
1972 /**
1973 * Gets a list of all the text items on the frame.
1974 *
1975 * @return
1976 */
1977 public Collection<Text> getTextItems() {
1978 Collection<Text> textItems = new ArrayList<Text>();
1979 for (Item i : getItems(true)) {
1980 // only add up normal body text items
1981 if ((i instanceof Text)) {
1982 textItems.add((Text) i);
1983 }
1984 }
1985 return textItems;
1986 }
1987
1988 public Text getAnnotation(String annotation) {
1989 if (_annotations == null)
1990 refreshAnnotationList();
1991
1992 return _annotations.get(annotation.toLowerCase());
1993 }
1994
1995 public void recalculate() {
1996 for (Item i : getItems()) {
1997 if (i.hasFormula() && !i.isAnnotation()) {
1998 i.calculate(i.getFormula());
1999 }
2000 }
2001 }
2002
2003 public void removeObserver(FrameObserver observer) {
2004 _observers.remove(observer);
2005 }
2006
2007 public void addObserver(FrameObserver observer) {
2008 _observers.add(observer);
2009 }
2010
2011 public void clearObservers() {
2012 for (FrameObserver fl : _observers) {
2013 fl.removeSubject(this);
2014 }
2015 // The frame listener will call the frames removeListener method
2016 assert (_observers.size() == 0);
2017 }
2018
2019 public Collection<Text> getNonAnnotationText(boolean removeTitle) {
2020 Collection<Text> items = new LinkedHashSet<Text>();
2021 for (Item i : getItems(true)) {
2022 // only add up normal body text items
2023 if (i instanceof Text && !i.isAnnotation()) {
2024 items.add((Text) i);
2025 }
2026 }
2027 if (removeTitle) {
2028 items.remove(getTitleItem());
2029 }
2030 return items;
2031 }
2032
2033 public void dispose() {
2034 clearObservers();
2035 for (Item i : _body) {
2036 i.dispose();
2037 }
2038 _frameName.dispose();
2039 _body = null;
2040 _frameName = null;
2041 }
2042
2043 public void parse() {
2044 for (Overlay o : getOverlays()) {
2045 o.Frame.parse();
2046 }
2047 // Must parse the frame AFTER the overlays
2048 FrameUtils.Parse(this);
2049 }
2050}
Note: See TracBrowser for help on using the repository browser.