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

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