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

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