source: trunk/src/org/expeditee/items/ItemUtils.java@ 10

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

Added Interactive Widget Items

File size: 19.0 KB
Line 
1package org.expeditee.items;
2
3import java.awt.Font;
4import java.awt.FontMetrics;
5import java.awt.Image;
6import java.awt.image.ImageObserver;
7import java.io.File;
8import java.io.UnsupportedEncodingException;
9import java.net.URL;
10import java.net.URLDecoder;
11import java.util.ArrayList;
12import java.util.LinkedList;
13import java.util.List;
14
15import javax.swing.JFrame;
16
17import org.expeditee.gui.DisplayIO;
18import org.expeditee.gui.Frame;
19
20//Static methods that provide functions for the objects\
21//mostly to transform values (string -> color etc).
22
23/**
24 * Static methods that provide functions for use in Items.
25 */
26public class ItemUtils {
27 private static JFrame _jf = new JFrame();
28
29 // Tag constants
30 public static final int TAG_SORT = 0;
31
32 public static final int TAG_JOIN = 1;
33
34 public static final int TAG_INDENT = 2;
35
36 public static final int TAG_OVERLAY = 3;
37
38 public static final int TAG_ACTIVE_OVERLAY = 4;
39
40 public static final int TAG_IMAGE = 5;
41
42 public static final int TAG_ITEM_TEMPLATE = 6;
43
44 public static final int TAG_ANNOTATION_TEMPLATE = 7;
45
46 public static final int TAG_CODE_COMMENT_TEMPLATE = 8;
47
48 public static final int TAG_MENU = 9;
49
50 public static final int TAG_MENU_NEXT = 10;
51
52 public static final int TAG_PARENT = 11;
53
54 public static final int TAG_LITERAL = 12;
55
56 public static final int TAG_FRAME_IMAGE = 13;
57
58 public static final int TAG_BACKUP = 14;
59
60 public static final int TAG_POINTTYPE = 15;
61
62 public static final int TAG_MIN = 0;
63
64 public static final int TAG_MAX = 15;
65
66 // Brook: Im claiming this number!
67 public static final int TAG_IWIDGET = 16;
68
69
70 /**
71 * Determines if the given List of Items contains an Item that is one of the
72 * pre-defined tags.
73 *
74 * @param items
75 * The list of Items to search through
76 * @param tag
77 * The Tag to search for, this should correspond to one of the
78 * predefined constants in this class
79 * @return True if an Item was found that is the given Tag, False otherwise.
80 */
81 public static boolean ContainsTag(List<Item> items, int tag) {
82 return ContainsTag(items, GetTag(tag));
83 }
84
85 public static boolean ContainsTag(List<Item> items, String tag) {
86 return (FindTag(items, tag) != null);
87 }
88
89 /**
90 * Searches the given List of Items for an Item that is one of the
91 * pre-defined tags.
92 *
93 * @param items
94 * The list of Items to search through
95 * @param tag
96 * The Tag to search for, this should correspond to one of the
97 * predefined constants in this class
98 * @return The Item that is the given tag if one is found, or False if none
99 * is found
100 */
101 public static Text FindTag(List<Item> items, int tag) {
102 return FindTag(items, GetTag(tag));
103 }
104
105 /**
106 * Searches the given List of Items for an Item that is the given tag
107 *
108 * @param items
109 * The list of Items to search through
110 * @param toFind
111 * The Tag to search for, this should include the at (@) symbol
112 * @return The Item that is the given tag if one is found, or False if none
113 * is found
114 */
115 public static Text FindTag(List<Item> items, String toFind) {
116 for (Item i : items) {
117 if (i instanceof Text && i.isAnnotation())
118 if (((Text) i).startsWith(toFind))
119 return (Text) i;
120 }
121
122 return null;
123 }
124
125 /**
126 * Determines if the given Item is one of the pre-defined tags in this class
127 *
128 * @param toCheck
129 * The Item to check
130 * @param tag
131 * The tag to check the Item against, this should correspond to
132 * one of the constants defined in this class
133 * @return True if the Item matches the given tag, false otherwise
134 */
135 public static boolean isTag(Item toCheck, int tag) {
136 /*
137 * if( !(toCheck instanceof Text) || !toCheck.isAnnotation()) return
138 * false;
139 *
140 * Text txt = (Text) toCheck; //tags are ase-insensitive
141 * if(String.CASE_INSENSITIVE_ORDER.compare(txt.getFirstLine(),GetTag(tag)) ==
142 * 0 || txt.startsWith(GetTag(tag) + " ")) return true;
143 *
144 * return false;
145 */
146 return isTag(toCheck, GetTag(tag));
147 }
148
149 /**
150 * Checks if the given Item contains the desired tag (case insensitive)
151 *
152 * @param toCheck
153 * The Item to check for the given tag
154 * @param tag
155 * The tag to check for in the given Item
156 * @return True if the tag is found in the given Item, False otherwise.
157 */
158 public static boolean isTag(Item toCheck, String tag) {
159 if (!(toCheck instanceof Text) || !toCheck.isAnnotation())
160 return false;
161
162 Text txt = (Text) toCheck;
163 // tags are ase-insensitive
164 if (String.CASE_INSENSITIVE_ORDER.compare(txt.getFirstLine(), tag) == 0
165 || txt.startsWith(tag + " "))
166 return true;
167
168 return false;
169 }
170
171 /**
172 * Strips off the given tag from the given String, and returns wathever is
173 * left
174 *
175 * @param toStrip
176 * The String to strip the Tag from
177 * @param tag
178 * The tag to remove from the String
179 * @return The String that results from removing the given Tag from the
180 * given String, or null if the given String is not the given Tag
181 */
182 public static String StripTag(String toStrip, String tag) {
183 if (toStrip.toLowerCase().startsWith(tag.toLowerCase())) {
184 toStrip = toStrip.substring(tag.length()).trim();
185 return toStrip;
186 }
187
188 /**
189 * TODO: Change this to use REGEX
190 */
191
192 return null;
193 }
194
195 /**
196 * The same as StripTag(String, String), but this method iterates through
197 * the list of pre-defined tags until one is found.
198 *
199 * @param toStrip
200 * The String to strip the tag from
201 * @return The String that results from stripping off the Tag, or null if
202 * the given String was not a tag
203 */
204 public static String StripTag(String toStrip) {
205 // there must be something left after stripping
206 if (toStrip == null)// || toStrip.trim().indexOf(" ") < 0)
207 return toStrip;
208
209 for (int i = TAG_MIN; i <= TAG_MAX; i++) {
210 String res = StripTag(toStrip, GetTag(i));
211 if (res != null)
212 return res;
213 }
214
215 return null;
216 }
217
218 /**
219 * Strips the first character from a string if it is the @ symbol
220 * @param toStrip
221 * the string to be stripped
222 * @return the stripped version of the string
223 */
224 public static String StripTagSymbol(String toStrip) {
225 // there must be something left after stripping
226 if (toStrip != null) {
227 if (toStrip.length() > 0) {
228 if (toStrip.charAt(0) == '@') {
229 return toStrip.substring(1);
230 }
231 }
232 }
233
234 return toStrip;
235 }
236
237 /**
238 * Converts the given int to the String tag. The int should correspond to
239 * one of the constants in this class, if it does not this method will
240 * return null.
241 *
242 * @param tag
243 * The int corresponding to the constants in this class of which
244 * tag to return
245 * @return The String representation of the given Tag, or null if the given
246 * value does not have a tag associated
247 */
248 public static String GetTag(int tag) {
249 switch (tag) {
250 case TAG_SORT:
251 return "@sort";
252 case TAG_JOIN:
253 return "@join";
254 case TAG_INDENT:
255 return "@indent";
256 case TAG_OVERLAY:
257 return "@o";
258 case TAG_ACTIVE_OVERLAY:
259 return "@ao";
260 case TAG_IMAGE:
261 return "@i:";
262 case TAG_ITEM_TEMPLATE:
263 return "@itemtemplate";
264 case TAG_ANNOTATION_TEMPLATE:
265 return "@annotationtemplate";
266 case TAG_CODE_COMMENT_TEMPLATE:
267 return "@commenttemplate";
268 case TAG_MENU:
269 return "@menu";
270 case TAG_MENU_NEXT:
271 return "@nextmenu";
272 case TAG_PARENT:
273 return "@parent";
274 case TAG_LITERAL:
275 return "@lit";
276 case TAG_FRAME_IMAGE:
277 return "@f";
278 case TAG_BACKUP:
279 return "@old";
280 case TAG_POINTTYPE:
281 return "@pointtype:";
282 case TAG_IWIDGET:
283 return "@iw";
284 default:
285 return null;
286 }
287 }
288
289 /**
290 * Returns a FontMetrics object that corresponds to the given Font.
291 *
292 * @param font
293 * The Font to derive the FontMetrics from
294 * @return FontMetrics that correspond to the given Font
295 */
296 public static FontMetrics getFontMetrics(Font font) {
297 return _jf.getFontMetrics(font);
298 }
299
300 public static Image CreateImage(int x, int y) {
301 return _jf.createImage(x, y);
302 }
303
304 /**
305 * Creates a picture object from the information stored in the given Text
306 * object. <br>
307 * The paths searched are in the following order:<br>
308 * /images/<br>
309 * the source text as a relative path (from program root folder). <br>
310 * the source text as an absolute path <br>
311 * <br>
312 * If the Image file cannot be found on disk null is returned.
313 *
314 * @param source
315 * The Text file containing the Picture infomation
316 * @return The Picture object representing the file, or Null if the file is
317 * not found.
318 */
319 public static Picture CreatePicture(Text source, ImageObserver observer) {
320 String text = source.getTextNoList();
321 String path = "";
322 String size = "";
323
324 try {
325 // remove @i tag
326 text = text.replaceFirst("@i:", "");
327 text = text.replaceAll("\n", "");
328 text = text.trim();
329
330 if (text.indexOf(".") >= text.lastIndexOf(" "))
331 path = text;
332 else
333 path = text.substring(0, text.lastIndexOf(" "));
334
335 size = text.substring(path.length()).trim();
336
337 // try images subdirectory
338 File file = null;
339
340 for (String dir : org.expeditee.gui.UserSettings.ImageDirs) {
341 file = new File(dir + path);
342 if (file.exists() && !file.isDirectory())
343 break;
344 }
345
346 if (file == null || !file.exists() || file.isDirectory())
347 file = new File(path);
348
349 // try relative path
350 if (!file.exists() || file.isDirectory()) {
351 URL picture = new Object().getClass().getResource(path);
352
353 // decode to remove %20 in windows folder names
354 if (picture != null) {
355 try {
356 path = URLDecoder.decode(picture.getFile(), "UTF-8");
357 } catch (UnsupportedEncodingException e) {
358 // TODO Auto-generated catch block
359 e.printStackTrace();
360 }
361 }
362
363 } else
364 path = file.getPath();
365
366 // if the image isn't found by now, give up.
367 file = new File(path);
368 if (!file.exists() || file.isDirectory()) {
369 return null;
370 }
371
372 } catch (Exception e) {
373 return null;
374 }
375
376 try {
377 Picture pic = new Picture(source, path, size, observer);
378
379 return pic;
380 } catch (Exception e) {
381 e.printStackTrace();
382 return null;
383 }
384
385 }
386
387 public static Picture CreateFramePicture(Text source, ImageObserver observer) {
388 String size = source.getFirstLine();
389
390 // remove @f tag
391 size = size.replaceFirst("@f", "");
392 size = size.trim();
393
394 try {
395 Picture pic = new Picture(source, null, size, observer);
396 return pic;
397 } catch (Exception e) {
398 //e.printStackTrace();
399 return null;
400 }
401 }
402
403 /**
404 * Creates a deep copy of the given List of Items.
405 *
406 * @param toCopy
407 * The list of Items to copy
408 * @return A list containing a copy of all Items in the given List
409 */
410 public static List<Item> CopyItems(List<Item> toCopy) {
411 return CopyItems(toCopy, false);
412 }
413 public static List<Item> CopyItems(List<Item> toCopy, boolean extrude) {
414 // The copies to return
415 List<Item> copies = new ArrayList<Item>();
416
417 // list of dots at the end of lines
418 List<Item> lineEnds = new ArrayList<Item>();
419 List<Line> lines = new ArrayList<Line>();
420
421 List<Item> vConst = new ArrayList<Item>();
422 List<Item> hConst = new ArrayList<Item>();
423
424 List<Item> seen = new ArrayList<Item>();
425
426 // Widgets are super special
427 List<InteractiveWidget> widgets = new ArrayList<InteractiveWidget>();
428
429 for (Item i : toCopy) {
430
431// BROOK
432 if (i instanceof WidgetCorner) { // dont add these
433
434 if (!widgets.contains(((WidgetCorner)i).getWidgetSource()))
435 widgets.add(((WidgetCorner)i).getWidgetSource());
436
437// BROOK
438 } else if (i instanceof WidgetEdge) { // dont add these
439
440 // lines are recreated later
441 } else if (i instanceof Line) {
442 Line line = (Line) i;
443 lines.add(line);
444
445 lineEnds.add(line.getEndItem());
446 lineEnds.add(line.getStartItem());
447
448 List<Constraint> consts = line.getStartItem().getConstraints();
449 if (consts != null) {
450 for (Constraint c : consts) {
451 if (c.contains(line.getEndItem())) {
452 if (c.getConstraintType() == Constraint.VERTICAL) {
453 vConst.add(line.getStartItem());
454 vConst.add(line.getEndItem());
455 } else {
456 hConst.add(line.getStartItem());
457 hConst.add(line.getEndItem());
458 }
459 }
460 }
461 }
462 } else {
463 seen.add(i);
464 }
465 }
466
467 // only dots that are not line ends are copied
468 seen.removeAll(lineEnds);
469
470 // copy all single dots
471 for (Item i : seen) {
472 Item copy = i.copy();
473
474 if (i.getParent() != null) {
475 // if this is the frame name, make sure the frame is saved (in
476 // case it is a TDFC frame)
477 if (i == i.getParent().getFrameNameItem())
478 i.getParent().setChanged(true);
479
480 // if this is the title of the frame, link it to the frame
481 if (i.getLink() == null && i == i.getParent().getTitle()
482 && toCopy.size() == 1) {
483 // save the frame after copying
484 i.getParent().setChanged(true);
485 copy.setLink(i.getParent().getFrameName());
486 }
487 }
488
489 copies.add(copy);
490 }
491
492 seen.clear();
493
494 // replace line ends with their copies
495 // this is done here so that copied lines can still share end points
496 for (Item i : lineEnds) {
497 // if a copy of this dot has not yet been created
498 // only copy dots that are in the list of dots to copy, leave
499 // others
500 // untouched
501 if (!seen.contains(i) && toCopy.contains(i)) {
502 // create a copy of the line end
503 Item copy = i.copy();
504
505 //copy.setID(DisplayIO.getCurrentFrame().getNextItemID());
506
507 copy.removeAllLines();
508 copy.removeAllConstraints();
509
510 // replace all other instances with the copy
511 ReplaceAll(lineEnds, i, copy);
512 ReplaceAll(vConst, i, copy);
513 ReplaceAll(hConst, i, copy);
514
515 if (extrude) {
516 Frame frame = DisplayIO.getCurrentFrame();
517 Line newLine = new Line(i,copy, frame.getNextItemID());
518 frame.addItem(newLine);
519 copies.add(newLine);
520 }
521
522 seen.add(i);
523 copies.add(copy);
524 }
525 }
526
527 // recreate lines
528 for (int i = 0; i < lineEnds.size(); i += 2) {
529 Line line = (Line) lines.get(i / 2).copy();
530
531 line.setID(DisplayIO.getCurrentFrame().getNextItemID());
532 line.setStartItem(lineEnds.get(i));
533 line.setEndItem(lineEnds.get(i + 1));
534
535 copies.add(line);
536 }
537
538 // recreate vertical constraints
539 for (int i = 0; i < vConst.size(); i += 2) {
540 new Constraint(vConst.get(i), vConst.get(i + 1), DisplayIO
541 .getCurrentFrame().getNextItemID(), Constraint.VERTICAL);
542 }
543
544 // recreate horizontal constraints
545 for (int i = 0; i < hConst.size(); i += 2) {
546 new Constraint(hConst.get(i), hConst.get(i + 1), DisplayIO
547 .getCurrentFrame().getNextItemID(), Constraint.HORIZONTAL);
548 }
549
550 // BROOK
551 for (InteractiveWidget iw : widgets) {
552 try {
553
554 InteractiveWidget icopy = iw.copy();
555 copies.addAll(icopy.getItems());
556
557 } catch (InteractiveWidgetNotAvailableException e) {
558 e.printStackTrace();
559 }
560 }
561
562 return copies;
563 }
564
565 private static void ReplaceAll(List<Item> list, Item replace, Item with) {
566 // replace all other instances with the copy
567 for (int pos = 0; pos < list.size(); pos++)
568 if (list.get(pos) == replace) {
569 list.set(pos, with);
570 }
571 }
572
573 /**
574 * Attempts to create a new line that starts from the given Item
575 * ('unreeling'). The Item must already have at least one line, and not be a
576 * line itself to be unreeled from.
577 *
578 * @param toUnreelFrom
579 * The Item that will be one end point of the new line
580 * @return A List containing the newly created Item and Line that unreel
581 * from the given Item, or null if this Item cannot be unreeled
582 * from.
583 */
584 public static List<Item> UnreelLine(Item toUnreelFrom) {
585 // the Item must already have one line to be unreeled from
586 if (toUnreelFrom == null || toUnreelFrom.getLines().size() < 1)
587 return null;
588
589 List<Item> unreel = new ArrayList<Item>(2);
590 unreel.add(toUnreelFrom);
591 unreel.addAll(toUnreelFrom.getLines());
592 return UnreelLine(unreel);
593 }
594
595 /**
596 * Attempts to create a new line that starts from the given list of Items
597 * ('unreeling'). The List must contain only one non-line Item. The non-line
598 * Item must already have at least one line to be unreeled from.
599 *
600 * @param toUnreel
601 * The List containing the Item that will be one end point of the
602 * new line
603 * @return A List of the newly created Item and Line that unreel from the
604 * Item in the given List, or null if this List cannot be unreeled
605 * from.
606 */
607 public static List<Item> UnreelLine(List<Item> toUnreel) {
608 Item origEnd = null;
609 // find the end being unreeled from
610 for (Item item : toUnreel) {
611 // we dont want to unreel anything other than lines
612 if (!(item instanceof Dot || item instanceof Line)) {
613 return null;
614 }
615 //find the dot to unreel from
616 if (item instanceof Dot) {
617 // if there are multiple ends in the list, return
618 if (origEnd != null)
619 return null;
620
621 origEnd = item;
622 }
623 }
624
625 // copy the original endpoint
626 Item copy = origEnd.copy();
627 copy.removeAllLines();
628 copy.removeAllConstraints();
629
630 int num = 0;
631
632 //Mike: WHAT IS THIS CODE DOING?!!?
633 for (Line line : origEnd.getLines())
634 num = Math.min(num, line.getID());
635
636 num--;
637
638 // create a new line
639 Line line = new Line(origEnd, copy, num);
640 //copy.setFloating(true);
641 origEnd.setArrowheadLength(0);
642 //copy.setArrowheadLength(0);
643
644 List<Item> toReturn = new LinkedList<Item>();
645 toReturn.add(copy);
646 toReturn.add(line);
647 return toReturn;
648 }
649
650 /**
651 * Checks through all Lines and Dots on the current Frame to detect if any
652 * form an enclosure, which can then be used to manipulate items within the
653 * polygon. If an enclosure is found, then the dots will have their
654 * enclosure value set to true, and a List is created that contains all the
655 * Dots in the order they were processed. Actual calculation of the Polygon
656 * is done dynamically (to account for Dots being moved).
657 */
658 public static void EnclosedCheck(List<Item> items) {
659 _seen.clear();
660
661 // get all dots on the Frame
662 List<Dot> dots = new ArrayList<Dot>(0);
663 for (Item i : items) {
664 if (i instanceof Dot) {
665 Dot d = (Dot) i;
666 d.setEnclosedList(null);
667
668 if (d.getLines().size() == 2)
669 dots.add(d);
670 }
671 }
672
673 // if there are no dots on the Frame, then there can't be an enclosure
674 if (dots.size() == 0)
675 return;
676
677 //TODO optimise this code!!
678 // iterate through all the dots
679 for (Dot searchFor : dots) {
680 _seen.clear();
681
682 for (Line l : searchFor.getLines()) {
683 _seen.add(l);
684 if (traverse(searchFor, l.getOppositeEnd(searchFor))) {
685 _path.add((Dot) l.getOppositeEnd(searchFor));
686
687 for (Dot d : _path)
688 d.setEnclosedList(_path);
689
690 _path = new ArrayList<Dot>(0);
691
692 break;
693 }
694 }
695 }
696
697 }
698
699 private static List<Line> _seen = new ArrayList<Line>();
700
701 private static List<Dot> _path = new ArrayList<Dot>();
702
703 private static boolean traverse(Dot toFind, Item searchFrom) {
704 if (toFind == null || searchFrom == null
705 || !(searchFrom instanceof Dot))
706 return false;
707
708 if (searchFrom.getLines().size() != 2)
709 return false;
710
711 if (toFind == searchFrom)
712 return true;
713
714 for (Line l : searchFrom.getLines()) {
715 if (!(_seen.contains(l))) {
716 _seen.add(l);
717 if (traverse(toFind, l.getOppositeEnd(searchFrom))) {
718 _path.add((Dot) l.getOppositeEnd(searchFrom));
719 return true;
720 }
721 }
722
723 }
724
725 return false;
726 }
727}
Note: See TracBrowser for help on using the repository browser.