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

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