source: trunk/src/org/expeditee/actions/Misc.java@ 581

Last change on this file since 581 was 581, checked in by jts21, 11 years ago

Start adding XML save format. Idea is to make special items (Images, Widgets, etc) possible to save without converting them to Text items first, and to make the save format more human-readable in general

File size: 40.9 KB
Line 
1package org.expeditee.actions;
2
3import java.awt.Color;
4import java.awt.Desktop;
5import java.awt.Image;
6import java.awt.Polygon;
7import java.awt.image.BufferedImage;
8import java.awt.image.VolatileImage;
9import java.io.File;
10import java.io.FileNotFoundException;
11import java.io.IOException;
12import java.io.StringWriter;
13import java.io.Writer;
14import java.lang.reflect.Method;
15import java.util.ArrayList;
16import java.util.Collection;
17import java.util.LinkedList;
18import java.util.List;
19
20import javax.imageio.ImageIO;
21import javax.script.ScriptEngine;
22import javax.script.ScriptEngineManager;
23
24import org.expeditee.gui.AttributeUtils;
25import org.expeditee.gui.Browser;
26import org.expeditee.gui.DisplayIO;
27import org.expeditee.gui.Frame;
28import org.expeditee.gui.FrameGraphics;
29import org.expeditee.gui.FrameIO;
30import org.expeditee.gui.FrameMouseActions;
31import org.expeditee.gui.FrameUtils;
32import org.expeditee.gui.FreeItems;
33import org.expeditee.gui.MessageBay;
34import org.expeditee.gui.Reminders;
35import org.expeditee.gui.TimeKeeper;
36import org.expeditee.importer.FrameDNDTransferHandler;
37import org.expeditee.io.JavaWriter;
38import org.expeditee.io.WebParser;
39import org.expeditee.io.XMLWriter;
40import org.expeditee.io.flowlayout.XGroupItem;
41import org.expeditee.items.Item;
42import org.expeditee.items.ItemUtils;
43import org.expeditee.items.Line;
44import org.expeditee.items.Text;
45import org.expeditee.items.widgets.InteractiveWidget;
46import org.expeditee.items.widgets.JfxBrowser;
47import org.expeditee.math.ExpediteeJEP;
48import org.expeditee.simple.SString;
49import org.expeditee.stats.CometStats;
50import org.expeditee.stats.DocumentStatsFast;
51import org.expeditee.stats.SessionStats;
52import org.expeditee.stats.StatsLogger;
53import org.expeditee.stats.TreeStats;
54import org.nfunk.jep.Node;
55import org.nfunk.jep.ParseException;
56
57
58
59/**
60 * A list of miscellaneous Actions and Actions specific to Expeditee
61 *
62 */
63public class Misc {
64
65 /**
66 * Causes the system to beep
67 */
68 public static void beep() {
69 java.awt.Toolkit.getDefaultToolkit().beep();
70 }
71
72 /**
73 * Returns an Item located at the specified position.
74 * kgas1 - 23/01/2012
75 * @param x
76 * @param y
77 * @return
78 */
79 public static Item getItemAtPosition(int x, int y, Frame f)
80 {
81 Frame current = f;
82 List<Item> allItems = current.getItems();
83
84 for(Item i : allItems)
85 {
86 if(i.getX() == x && i.getY() == y)
87 return i;
88 }
89
90 return null;
91 }
92
93 /**
94 * Returns an item containing a specified piece of data.
95 * kgas1 - 7/06/2012
96 * @param s
97 * @param f
98 * @return
99 */
100 public static Item getItemContainingData(String s, Frame f){
101
102 Frame current = f;
103
104 List<Item> allItems = current.getItems();
105
106
107 for(Item i : allItems){
108
109
110 if(i.getData() != null && i.getData().size() > 0){
111 if(i.getData().contains(s)){
112 return i;
113 }
114 }
115 }
116
117 return null;
118 }
119
120 /**
121 * Forces a repaint of the current Frame
122 */
123 public static void display() {
124 FrameGraphics.refresh(false);
125 }
126
127 public static String getWindowSize() {
128 return Browser.getWindows()[0].getSize().toString();
129 }
130
131 /**
132 * Restores the current frame to the last saved version currently on the
133 * hard disk
134 */
135 public static void restore() {
136 FrameIO.Reload();
137 // MessageBay.displayMessage("Restoration complete.");
138 }
139
140 /**
141 * Toggles AudienceMode on or off
142 */
143 public static void toggleAudienceMode() {
144 FrameGraphics.ToggleAudienceMode();
145 }
146
147 /**
148 * Toggles TwinFrames mode on or off
149 */
150 public static void toggleTwinFramesMode() {
151 DisplayIO.ToggleTwinFrames();
152 }
153
154 /**
155 * If the given Item is a Text Item, then the text of the Item is
156 * interpreted as actions, if not this method does nothing.
157 *
158 * @param current
159 * The Item to read the Actions from
160 */
161 public static void runItem(Item current) throws Exception {
162 if (current instanceof Text) {
163 List<String> actions = ((Text) current).getTextList();
164 for (String action : actions) {
165 if (!action.equalsIgnoreCase("runitem")) {
166 Actions.PerformAction(DisplayIO.getCurrentFrame(), current,
167 action);
168 }
169 }
170 } else {
171 MessageBay.errorMessage("Item must be a text item.");
172 }
173 }
174
175 /**
176 * Prompts the user to confirm deletion of the current Frame, and deletes if
177 * the user chooses. After deletion this action calls back(), to ensure the
178 * deleted frame is not still being shown
179 *
180 */
181 public static void DeleteFrame(Frame toDelete) {
182 String deletedFrame = toDelete.getName();
183 String deletedFrameNameLowercase = deletedFrame.toLowerCase();
184 String errorMessage = "Error deleting " + deletedFrame;
185 try {
186 String deletedFrameName = FrameIO.DeleteFrame(toDelete);
187 if (deletedFrameName != null) {
188 DisplayIO.Back();
189 // Remove any links on the previous frame to the one being
190 // deleted
191 Frame current = DisplayIO.getCurrentFrame();
192 for (Item i : current.getItems())
193 if (i.getLink() != null
194 && i.getAbsoluteLink().toLowerCase().equals(
195 deletedFrameNameLowercase)) {
196 i.setLink(null);
197 }
198 MessageBay.displayMessage(deletedFrame + " renamed "
199 + deletedFrameName);
200 // FrameGraphics.Repaint();
201 return;
202 }
203 } catch (IOException ioe) {
204 if (ioe.getMessage() != null)
205 errorMessage += ". " + ioe.getMessage();
206 } catch (SecurityException se) {
207 if (se.getMessage() != null)
208 errorMessage += ". " + se.getMessage();
209 } catch (Exception e) {
210 e.printStackTrace();
211 }
212 MessageBay.errorMessage(errorMessage);
213 }
214
215 /**
216 * Loads the Frame linked to by the given Item. The first Item on the Frame
217 * that is not the title or name is then placed on the cursor. If the given
218 * Item has no link, or no item is found then this is a no-op.
219 *
220 * @param current
221 * The Item that links to the Frame that the Item will be loaded
222 * from.
223 */
224 public static Item GetItemFromChildFrame(Item current) {
225 return getFromChildFrame(current, false);
226 }
227
228 public static void GetItemsFromChildFrame(Item current) {
229 getItemsFromChildFrame(current, false);
230 }
231
232 /**
233 * Loads the Frame linked to by the given Item. The first Text Item on the
234 * Frame that is not the title or name is then placed on the cursor. If the
235 * given Item has no link, or no item is found then this is a no-op.
236 *
237 * @param current
238 * The Item that links to the Frame that the Item will be loaded
239 * from.
240 */
241 public static Item GetTextFromChildFrame(Item current) {
242 return getFromChildFrame(current, true);
243 }
244
245 private static Item getFromChildFrame(Item current, boolean textOnly) {
246 Item item = getFirstBodyItemOnChildFrame(current, textOnly);
247 // if no item was found
248 if (item != null) {
249 // copy the item and switch
250 item = item.copy();
251 item.setPosition(DisplayIO.getMouseX(), FrameMouseActions.getY());
252 }
253 return item;
254 }
255
256 private static void getItemsFromChildFrame(Item current, boolean textOnly) {
257 Collection<Item> items = getItemsOnChildFrame(current, textOnly);
258 // if no item was found
259 if (items == null || items.size() == 0) {
260 return;
261 }
262
263 // copy the item and switch
264 Collection<Item> copies = ItemUtils.CopyItems(items);
265 Item first = items.iterator().next();
266 float deltaX = DisplayIO.getMouseX() - first.getX();
267 float deltaY = FrameMouseActions.getY() - first.getY();
268 for (Item i : copies) {
269 if (i.isVisible())
270 i.setXY(i.getX() + deltaX, i.getY() + deltaY);
271 i.setParent(null);
272 }
273 FrameMouseActions.pickup(copies);
274 FrameGraphics.Repaint();
275 }
276
277 /**
278 * Sets the given Item to have the Given Color. Color can be null (for
279 * default)
280 *
281 * @param toChange
282 * The Item to set the Color.
283 * @param toUse
284 * The Color to give the Item.
285 */
286 public static void SetItemBackgroundColor(Item toChange, Color toUse) {
287 if (toChange == null)
288 return;
289
290 toChange.setBackgroundColor(toUse);
291 FrameGraphics.Repaint();
292 }
293
294 /**
295 * Sets the given Item to have the Given Color. Color can be null (for
296 * default)
297 *
298 * @param toChange
299 * The Item to set the Color.
300 * @param toUse
301 * The Color to give the Item.
302 */
303 public static void SetItemColor(Item toChange, Color toUse) {
304 if (toChange == null)
305 return;
306
307 toChange.setColor(toUse);
308 FrameGraphics.Repaint();
309 }
310
311 /**
312 * Creates a new Text Object containing general statistics for the current
313 * session. The newly created Text Object is then attached to the cursor via
314 * FrameMouseActions.pickup(Item)
315 */
316 public static void GetSessionStats() {
317 attachStatsToCursor(SessionStats.getCurrentStats());
318 }
319
320 /**
321 * Creates a new Text Object containing statistics for the current tree.
322 */
323 public static String GetCometStats(Frame frame) {
324 TimeKeeper timer = new TimeKeeper();
325 MessageBay.displayMessage("Computing comet stats...");
326 CometStats cometStats = new CometStats(frame);
327 String result = cometStats.toString();
328 MessageBay.overwriteMessage("Comet stats time: "
329 + timer.getElapsedStringSeconds());
330 return result;
331 }
332
333 public static String GetTreeStats(Frame frame) {
334 TimeKeeper timer = new TimeKeeper();
335 MessageBay.displayMessage("Computing tree stats...");
336
337 TreeStats treeStats = new TreeStats(frame);
338 String result = treeStats.toString();
339 MessageBay.overwriteMessage("Tree stats time: "
340 + timer.getElapsedStringSeconds());
341 return result;
342
343 }
344
345 public static String GetDocumentStats(Frame frame) {
346 TimeKeeper timer = new TimeKeeper();
347 MessageBay.displayMessage("Computing document stats...");
348 FrameIO.ForceSaveFrame(frame);
349 DocumentStatsFast docStats = new DocumentStatsFast(frame.getName(),
350 frame.getTitle());
351 String result = docStats.toString();
352
353 MessageBay.overwriteMessage("Document stats time: "
354 + timer.getElapsedStringSeconds());
355 return result;
356
357 }
358
359 /**
360 * Creates a text item and attaches it to the cursor.
361 *
362 * @param itemText
363 * the text to attach to the cursor
364 */
365 public static void attachStatsToCursor(String itemText) {
366 SessionStats.CreatedText();
367 Frame current = DisplayIO.getCurrentFrame();
368 Item text = current.getStatsTextItem(itemText);
369 FrameMouseActions.pickup(text);
370 FrameGraphics.Repaint();
371 }
372
373 public static void attachTextToCursor(String itemText) {
374 SessionStats.CreatedText();
375 Frame current = DisplayIO.getCurrentFrame();
376 Item text = current.getTextItem(itemText);
377 FrameMouseActions.pickup(text);
378 FrameGraphics.Repaint();
379 }
380
381 /**
382 * Creates a new Text Object containing statistics for moving, deleting and
383 * creating items in the current session. The newly created Text Object is
384 * then attached to the cursor via FrameMouseActions.pickup(Item)
385 */
386 public static String getItemStats() {
387 return SessionStats.getItemStats();
388 }
389
390 /**
391 * Creates a new Text Object containing statistics for the time between
392 * events triggered by the user through mouse clicks and key presses. The
393 * newly created Text Object is then attached to the cursor via
394 * FrameMouseActions.pickup(Item)
395 */
396 public static String getEventStats() {
397 return SessionStats.getEventStats();
398 }
399
400 /**
401 * Creates a new Text Object containing the contents of the current frames
402 * file.
403 */
404 public static String getFrameFile(Frame frame) {
405 return FrameIO.ForceSaveFrame(frame);
406 }
407
408 /**
409 * Creates a new Text Object containing the available fonts.
410 */
411 public static String getFontNames() {
412 Collection<String> availableFonts = Actions.getFonts().values();
413 StringBuilder fontsList = new StringBuilder();
414 for (String s : availableFonts) {
415 fontsList.append(s).append(Text.LINE_SEPARATOR);
416 }
417 fontsList.deleteCharAt(fontsList.length() - 1);
418
419 return fontsList.toString();
420 }
421
422 public static String getUnicodeCharacters(int start, int finish) {
423 if (start < 0 && finish < 0) {
424 throw new RuntimeException("Parameters must be non negative");
425 }
426 // Swap the start and finish if they are inthe wrong order
427 if (start > finish) {
428 start += finish;
429 finish = start - finish;
430 start = start - finish;
431 }
432 StringBuilder charList = new StringBuilder();
433 int count = 0;
434 charList.append(String.format("Unicode block 0x%x - 0x%x", start,
435 finish));
436 System.out.println();
437 // charList.append("Unicode block: ").append(String.format(format,
438 // args))
439 for (char i = (char) start; i < (char) finish; i++) {
440 if (Character.isDefined(i)) {
441 if (count++ % 64 == 0)
442 charList.append(Text.LINE_SEPARATOR);
443 charList.append(Character.valueOf(i));
444 }
445 }
446 return charList.toString();
447 }
448
449 /**
450 * Gets a single block of Unicode characters.
451 *
452 * @param start
453 * the start of the block
454 */
455 public static String getUnicodeCharacters(int start) {
456 return getUnicodeCharacters(start, start + 256);
457 }
458
459 public static String getMathSymbols() {
460 return getUnicodeCharacters('\u2200', '\u2300');
461 }
462
463 /**
464 * Resets the statistics back to zero.
465 */
466 public static void repaint() {
467 StatsLogger.WriteStatsFile();
468 SessionStats.resetStats();
469 }
470
471 /**
472 * Loads a frame with the given name and saves it as a JPEG image.
473 *
474 * @param framename
475 * The name of the Frame to save
476 */
477 public static void jpegFrame(String framename) {
478 ImageFrame(framename, "JPEG");
479 }
480
481 /**
482 * Saves the current frame as a JPEG image. This is the same as calling
483 * JpegFrame(currentFrame.getName())
484 */
485 public static void jpegFrame() {
486 ImageFrame(DisplayIO.getCurrentFrame().getName(), "JPEG");
487 }
488
489 public static void jpgFrame() {
490 jpegFrame();
491 }
492
493 /**
494 * Loads a frame with the given name and saves it as a PNG image.
495 *
496 * @param framename
497 * The name of the Frame to save
498 */
499 public static void PNGFrame(String framename) {
500 ImageFrame(framename, "PNG");
501 }
502
503 /**
504 * Saves the current frame as a PNG image. This is the same as calling
505 * PNGFrame(currentFrame.getName())
506 */
507 public static void PNGFrame(Frame frame) {
508 ImageFrame(frame.getName(), "PNG");
509 }
510
511 public static String SaveImage(BufferedImage screen, String format,
512 String directory, String fileName) {
513 String suffix = "." + format.toLowerCase();
514 String shortFileName = fileName;
515 // Check if we need to append the suffix
516 if (fileName.indexOf('.') < 0)
517 fileName += suffix;
518 else
519 shortFileName = fileName.substring(0, fileName.length() - suffix.length());
520
521 try {
522 int count = 2;
523 // set up the file for output
524 File out = new File(directory + fileName);
525 while (out.exists()) {
526 fileName = shortFileName + "_" + count++ + suffix;
527 out = new File(directory + fileName);
528 }
529
530 if (!out.getParentFile().exists())
531 out.mkdirs();
532
533 // If the image is successfully written out return the fileName
534 if (ImageIO.write(screen, format, out))
535 return fileName;
536
537 } catch (Exception e) {
538 e.printStackTrace();
539 }
540 return null;
541 }
542
543 public static String ImageFrame(Frame frame, String format, String directory) {
544 assert (frame != null);
545
546 Image oldBuffer = frame.getBuffer();
547 frame.setBuffer(null);
548 // Jpeg only works properly with volitile frames
549 // Png transparency only works with bufferedImage form
550 Image frameBuffer = FrameGraphics.getBuffer(frame, false, format
551 .equalsIgnoreCase("jpeg"));
552 // Make sure overlay stuff doesnt disapear on the frame visible on the
553 // screen
554 frame.setBuffer(oldBuffer);
555 BufferedImage screen = null;
556
557 if (frameBuffer instanceof VolatileImage) {
558 // If its the current frame it will be a volitive image
559 screen = ((VolatileImage) frameBuffer).getSnapshot();
560 } else {
561 assert (frameBuffer instanceof BufferedImage);
562 screen = (BufferedImage) frameBuffer;
563 }
564 return SaveImage(screen, format, directory, frame.getExportFileName());
565 }
566
567 /**
568 * Saves the Frame with the given Framename as an image of the given format.
569 *
570 * @param framename
571 * The name of the Frame to save as an image
572 * @param format
573 * The Image format to use (i.e. "PNG", "BMP", etc)
574 */
575 public static void ImageFrame(String framename, String format) {
576 Frame loaded = FrameIO.LoadFrame(framename);
577
578 // if the frame was loaded successfully
579 if (loaded != null) {
580 String path = FrameIO.EXPORTS_DIR;
581 String frameName = ImageFrame(loaded, format, path);
582 if (frameName != null)
583 MessageBay.displayMessage("Frame successfully saved to " + path
584 + frameName);
585 else
586 MessageBay.errorMessage("Could not find image writer for "
587 + format + " format");
588 // if the frame was not loaded successfully, alert the user
589 } else {
590 MessageBay.displayMessage("Frame '" + framename
591 + "' could not be found.");
592 }
593 }
594
595 public static void MessageLn(Item message) {
596 if (message instanceof Text)
597 MessageBay.displayMessage((Text) message);
598 }
599
600 /**
601 * Displays a message in the message box area.
602 *
603 * @param message
604 * the message to display
605 */
606 public static void MessageLn(String message) {
607 MessageBay.displayMessage(message);
608 }
609
610 public static void MessageLn2(String message, String message2) {
611 MessageBay.displayMessage(message + " " + message2);
612 }
613
614 public static void CopyFile(String existingFile, String newFileName) {
615 try {
616 // TODO is there a built in method which will do this faster?
617
618 MessageBay.displayMessage("Copying file " + existingFile + " to "
619 + newFileName + "...");
620 FrameIO.copyFile(existingFile, newFileName);
621 MessageBay.displayMessage("File copied successfully");
622 } catch (FileNotFoundException e) {
623 MessageBay.displayMessage("Error opening file: " + existingFile);
624 } catch (Exception e) {
625 MessageBay.displayMessage("File could not be copied");
626 }
627 }
628
629 /**
630 * Runs two methods alternatively a specified number of times and reports on
631 * the time spent running each method.
632 *
633 * @param fullMethodNameA
634 * @param fullMethodNameB
635 * @param repsPerTest
636 * the number of time each method is run per test
637 * @param tests
638 * the number of tests to conduct
639 *
640 */
641 public static void CompareMethods(String fullMethodNameA,
642 String fullMethodNameB, int repsPerTest, int tests) {
643 try {
644 String classNameA = getClassName(fullMethodNameA);
645 String classNameB = getClassName(fullMethodNameB);
646 String methodNameA = getMethodName(fullMethodNameA);
647 String methodNameB = getMethodName(fullMethodNameB);
648
649 Class<?> classA = Class.forName(classNameA);
650 Class<?> classB = Class.forName(classNameB);
651 Method methodA = classA.getDeclaredMethod(methodNameA,
652 new Class[] {});
653 Method methodB = classB.getDeclaredMethod(methodNameB,
654 new Class[] {});
655 TimeKeeper timeKeeper = new TimeKeeper();
656 long timeA = 0;
657 long timeB = 0;
658 // Run the tests
659 for (int i = 0; i < tests; i++) {
660 // Test methodA
661 timeKeeper.restart();
662 for (int j = 0; j < repsPerTest; j++) {
663 methodA.invoke((Object) null, new Object[] {});
664 }
665 timeA += timeKeeper.getElapsedMillis();
666 timeKeeper.restart();
667 // Test methodB
668 for (int j = 0; j < repsPerTest; j++) {
669 methodB.invoke((Object) null, new Object[] {});
670 }
671 timeB += timeKeeper.getElapsedMillis();
672 }
673
674 float aveTimeA = timeA * 1000F / repsPerTest / tests;
675 float aveTimeB = timeB * 1000F / repsPerTest / tests;
676 // Display Results
677 MessageBay.displayMessage("Average Execution Time");
678 MessageBay.displayMessage(methodNameA + ": "
679 + TimeKeeper.Formatter.format(aveTimeA) + "us");
680 MessageBay.displayMessage(methodNameB + ": "
681 + TimeKeeper.Formatter.format(aveTimeB) + "us");
682 } catch (Exception e) {
683 MessageBay.errorMessage(e.getClass().getSimpleName() + ": "
684 + e.getMessage());
685 }
686 }
687
688 public static String getClassName(String fullMethodName) {
689 assert (fullMethodName != null);
690 assert (fullMethodName.length() > 0);
691 int lastPeriod = fullMethodName.lastIndexOf('.');
692 if (lastPeriod > 0 && lastPeriod < fullMethodName.length() - 1)
693 return fullMethodName.substring(0, lastPeriod);
694 throw new RuntimeException("Invalid method name: " + fullMethodName);
695 }
696
697 public static String getMethodName(String methodName) {
698 assert (methodName != null);
699 assert (methodName.length() > 0);
700 int lastPeriod = methodName.lastIndexOf('.');
701 if (lastPeriod > 0 && lastPeriod < methodName.length() - 1)
702 return methodName.substring(1 + lastPeriod);
703 throw new RuntimeException("Invalid method name: " + methodName);
704 }
705
706 /**
707 * Loads the Frame linked to by the given Item. The first Item on the Frame
708 * that is not the title or name is then placed on the current frame. The
709 * item that was clicked on is placed on the frame it was linked to and the
710 * link is switched to the item from the child frame. If the given Item has
711 * no link, or no item is found then this is a no-op.
712 *
713 * @param current
714 * The Item that links to the Frame that the Item will be loaded
715 * from.
716 */
717 public static void SwapItemWithItemOnChildFrame(Item current) {
718 Item item = getFirstBodyItemOnChildFrame(current, false);
719 // if no item was found
720 if (item == null) {
721 return;
722 }
723
724 // swap the items parents
725 Frame parentFrame = current.getParent();
726 Frame childFrame = item.getParent();
727 current.setParent(childFrame);
728 item.setParent(parentFrame);
729
730 // swap the items on the frames
731 parentFrame.removeItem(current);
732 childFrame.removeItem(item);
733 parentFrame.addItem(item);
734 childFrame.addItem(current);
735
736 // swap the items links
737 item.setActions(current.getAction());
738 item.setLink(childFrame.getName());
739 current.setLink(parentFrame.getName());
740 // current.setLink(null);
741 current.setActions(null);
742
743 FrameGraphics.Repaint();
744 }
745
746 private static Item getFirstBodyItemOnChildFrame(Item current,
747 boolean textOnly) {
748 // the item must link to a frame
749 if (current.getLink() == null) {
750 MessageBay
751 .displayMessage("Cannot get item from child - this item has no link");
752 return null;
753 }
754
755 Frame child = FrameIO.LoadFrame(current.getAbsoluteLink());
756
757 // if the frame could not be loaded
758 if (child == null) {
759 MessageBay.errorMessage("Could not load child frame.");
760 return null;
761 }
762
763 // find the first non-title and non-name item
764 List<Item> body = new ArrayList<Item>();
765 if (textOnly)
766 body.addAll(child.getBodyTextItems(false));
767 else
768 body.addAll(child.getItems());
769 Item item = null;
770
771 for (Item i : body)
772 if (i != child.getTitleItem() && !i.isAnnotation()) {
773 item = i;
774 break;
775 }
776
777 // if no item was found
778 if (item == null) {
779 MessageBay.displayMessage("No item found to copy");
780 return null;
781 }
782
783 return item;
784 }
785
786 private static Collection<Item> getItemsOnChildFrame(Item current,
787 boolean textOnly) {
788 // the item must link to a frame
789 if (current.getLink() == null) {
790 MessageBay
791 .displayMessage("Cannot get item from child - this item has no link");
792 return null;
793 }
794 Frame child = FrameIO.LoadFrame(current.getAbsoluteLink());
795
796 // if the frame could not be loaded
797 if (child == null) {
798 MessageBay.errorMessage("Could not load child frame.");
799 return null;
800 }
801
802 // find the first non-title and non-name item
803 Collection<Item> body = new ArrayList<Item>();
804 if (textOnly)
805 body.addAll(child.getBodyTextItems(false));
806 else
807 body.addAll(child.getItems());
808
809 return body;
810 }
811
812 public static void calculate(Frame frame, Item toCalculate) {
813 if (toCalculate instanceof Text) {
814 Text text = (Text) toCalculate;
815 ExpediteeJEP myParser = new ExpediteeJEP();
816 myParser.addVariables(frame);
817 String linkedFrame = toCalculate.getAbsoluteLink();
818 if (linkedFrame != null) {
819 myParser.addVariables(FrameIO.LoadFrame(linkedFrame));
820 }
821 myParser.resetObserver();
822
823 // Do the calculation
824 String formulaFullCase = text.getText().replace('\n', ' ');
825 String formula = formulaFullCase.toLowerCase();
826
827 try {
828 Node node = myParser.parse(formula);
829 Object result = myParser.evaluate(node);
830 text.setText(result.toString(), true);
831 text.setFormula(formulaFullCase);
832 if (text.isFloating()) {
833 text.setPosition(FrameMouseActions.MouseX,
834 FrameMouseActions.MouseY);
835 FrameMouseActions.resetOffset();
836 } else {
837 text.getParentOrCurrentFrame().change();
838 }
839 } catch (ParseException e) {
840 MessageBay.errorMessage("Parse error "
841 + e.getMessage().replace("\n", ""));
842 } catch (Exception e) {
843 MessageBay.errorMessage("evaluation error "
844 + e.getMessage().replace("\n", ""));
845 e.printStackTrace();
846 }
847 }
848 }
849
850 /**
851 * Attach an item to the cursor.
852 *
853 * @param item
854 */
855 public static void attachToCursor(Item item) {
856 item.setParent(null);
857 FrameMouseActions.pickup(item);
858 FrameGraphics.Repaint();
859 }
860
861 public static void attachToCursor(Collection<Item> items) {
862 for (Item i : items) {
863 i.setParent(null);
864 i.invalidateAll();
865 }
866 FrameMouseActions.pickup(items);
867 // TODO figure out why this isnt repainting stuff immediately
868 // All of text item doesnt repaint until the cursor is moved
869 FrameGraphics.requestRefresh(true);
870 }
871
872 public static void importFiles(Item item) {
873 List<File> files = new LinkedList<File>();
874 for (String s : item.getText().split("\\s+")) {
875 File file = new File(s.trim());
876 if (file.exists()) {
877 files.add(file);
878 }
879 }
880 try {
881 FrameDNDTransferHandler.getInstance().importFileList(files,
882 FrameMouseActions.getPosition());
883 } catch (Exception e) {
884 }
885 }
886
887 public static void importFile(Item item) {
888 File file = new File(item.getText().trim());
889 if (file.exists()) {
890 try {
891 FrameDNDTransferHandler.getInstance().importFile(file,
892 FrameMouseActions.getPosition());
893 } catch (Exception e) {
894 e.printStackTrace();
895 }
896 }
897 }
898
899 public static Item createPolygon(Item item, int sides) {
900 if (item instanceof Text) {
901 try {
902 SString s = new SString(item.getText());
903 sides = s.integerValue().intValue();
904 } catch (NumberFormatException e) {
905 }
906 }
907
908 if (sides < 3) {
909 MessageBay.errorMessage("Shapes must have at least 3 sides");
910 }
911 double angle = -(180 - ((sides - 2) * 180.0F) / sides);
912 double curAngle = 0;
913 double size = 50F;
914 if (item.isLineEnd() && item.getLines().size() > 0) {
915 item = item.getLines().get(0);
916 }
917 // Use line length to determine the size of the shape
918 if (item instanceof Line) {
919 size = ((Line) item).getLength();
920 }
921
922 float curX = FrameMouseActions.MouseX;
923 float curY = FrameMouseActions.MouseY;
924
925 Collection<Item> newItems = new LinkedList<Item>();
926 Item[] d = new Item[sides];
927 // create dots
928 Frame current = DisplayIO.getCurrentFrame();
929 for (int i = 0; i < d.length; i++) {
930 d[i] = current.createDot();
931 newItems.add(d[i]);
932 d[i].setPosition(curX, curY);
933 curX += (float) (Math.cos((curAngle) * Math.PI / 180.0) * size);
934 curY += (float) (Math.sin((curAngle) * Math.PI / 180.0) * size);
935
936 curAngle += angle;
937 }
938 // create lines
939 for (int i = 1; i < d.length; i++) {
940 newItems.add(new Line(d[i - 1], d[i], current.getNextItemID()));
941 }
942 newItems.add(new Line(d[d.length - 1], d[0], current.getNextItemID()));
943
944 current.addAllItems(newItems);
945 if (item instanceof Text) {
946 for (Item i : item.getAllConnected()) {
947 if (i instanceof Line) {
948 item = i;
949 break;
950 }
951 }
952 }
953
954 Color newColor = item.getColor();
955 if (newColor != null) {
956 d[0].setColor(item.getColor());
957 if (item instanceof Text && item.getBackgroundColor() != null) {
958 d[0].setFillColor(item.getBackgroundColor());
959 } else {
960 d[0].setFillColor(item.getFillColor());
961 }
962 }
963 float newThickness = item.getThickness();
964 if (newThickness > 0) {
965 d[0].setThickness(newThickness);
966 }
967
968 ItemUtils.EnclosedCheck(newItems);
969 FrameGraphics.refresh(false);
970
971 return d[0];
972 }
973
974 public static void StopReminder() {
975 Reminders.stop();
976 }
977
978 public static void print(String file) {
979 try {
980 if (Browser._theBrowser.isMinimumVersion6()) {
981 if (Desktop.isDesktopSupported()) {
982 Desktop.getDesktop().print(new File(file));
983 }
984 }
985 } catch (Exception e) {
986 MessageBay.errorMessage("Printing error: " + e.getMessage());
987 }
988 }
989
990 public static int wordCount(String paragraph) {
991 return paragraph.trim().split("\\s+").length + 1;
992 }
993
994 public static int wordCount(Frame frame) {
995 int count = 0;
996
997 for (Text t : frame.getBodyTextItems(false)) {
998 count += wordCount(t.getText());
999 }
1000
1001 return count;
1002 }
1003
1004 public static void moveToPublic(Frame frame) {
1005 FrameIO.moveFrameset(frame.getFramesetName(), FrameIO.PUBLIC_PATH);
1006 }
1007
1008 public static void moveToPrivate(Frame frame) {
1009 FrameIO.moveFrameset(frame.getFramesetName(), FrameIO.FRAME_PATH);
1010 }
1011
1012 /**
1013 * Returns the value of a specified item attribute.
1014 *
1015 * @param item
1016 * from which to extract the value
1017 * @param attribute
1018 * name of an items attribute
1019 * @return the value of the attribute
1020 */
1021 public static String extract(Item item, String attribute) {
1022 return AttributeUtils.getAttribute(item, attribute);
1023 }
1024
1025 /**
1026 * Launches items.widgets.Browser and uses Text item as URL.
1027 * @param text Text item which passes contents as URL for browser.
1028 * @throws Exception
1029 */
1030 public static void startLoboBrowser(Item text) throws Exception {
1031 if (!(text instanceof Text)) {
1032 MessageBay.errorMessage("Must be a text item.");
1033 return;
1034 }
1035 if(text.getLink() != null) {
1036 MessageBay.errorMessage("Text item cannot have link.");
1037 return;
1038 }
1039
1040 FreeItems.getInstance().clear(); // remove url text from cursor
1041
1042 Text wt = new Text("@iw:org.expeditee.items.widgets.Browser"); // create new text item for browser widget
1043 wt.setParent(DisplayIO.getCurrentFrame()); // set parent of text source for InteractiveWidget.createWidget()
1044 wt.setXY(FrameMouseActions.getX(), FrameMouseActions.getY());
1045 // create widget from text item
1046 org.expeditee.items.widgets.Browser browser = (org.expeditee.items.widgets.Browser) InteractiveWidget.createWidget(wt);
1047
1048 if(FreeItems.textOnlyAttachedToCursor()) { // navigates to url specified by the text item
1049 browser.navigate(text.getText());
1050 } else {
1051 browser.navigate("http://www.waikato.ac.nz");
1052 }
1053
1054 FrameMouseActions.pickup(browser.getItems()); // attach browser widget to mouse
1055 }
1056
1057 /**
1058 * Text item becomes link to new frame containing items.widgets.Browser and uses Text item as URL for browser.
1059 * @param text Text item which passes contents as URL for browser and becomes link to the browser's new frame.
1060 * @throws Exception
1061 */
1062 public static void startLoboBrowserNewFrame(Item text) throws Exception {
1063 if (!(text instanceof Text)) {
1064 MessageBay.errorMessage("Must be a text item.");
1065 return;
1066 }
1067 if(text.getLink() != null) { // text item can't already have a link
1068 MessageBay.errorMessage("Text item already has link.");
1069 return;
1070 }
1071
1072 // Create new frame and text item for browser widget and parse created frame; loads browser widget
1073 Frame frame = FrameIO.CreateNewFrame(text);
1074 frame.addText(0, 50, "@iw:org.expeditee.items.widgets.Browser", null);
1075 FrameUtils.Parse(frame);
1076
1077 for(InteractiveWidget iw : frame.getInteractiveWidgets()) { // may be other widgets on frame
1078 if(iw instanceof org.expeditee.items.widgets.Browser) {
1079 // Set browser to 'full screen'
1080 iw.setSize(-1, -1, -1, -1, Browser._theBrowser.getWidth(), Browser._theBrowser.getHeight()
1081 - MessageBay.MESSAGE_BUFFER_HEIGHT - 80);
1082
1083 // If there is a text item attached to cursor use it as url for browser
1084 if(FreeItems.textOnlyAttachedToCursor()) {
1085 text.setLink("" + frame.getNumber());
1086 ((org.expeditee.items.widgets.Browser)iw).navigate(text.getText());
1087 } else {
1088 // Navigate to www.waikato.ac.nz by default if no url supplied and create new text item to be the link
1089 ((org.expeditee.items.widgets.Browser)iw).navigate("http://www.waikato.ac.nz");
1090 Text t = new Text("http://www.waikato.ac.nz");
1091 t.setParent(DisplayIO.getCurrentFrame()); // set parent of text source for InteractiveWidget.createWidget()
1092 t.setXY(FrameMouseActions.getX(), FrameMouseActions.getY());
1093 t.setLink("" + frame.getNumber()); // link url text to new browser frame
1094 FrameMouseActions.pickup(t); // Attach new text link to cursor
1095 }
1096 }
1097 }
1098
1099 FrameIO.SaveFrame(frame); // save frame to disk
1100 }
1101
1102 /**
1103 * Launches items.widgets.JfxBrowser and uses Text item as URL.
1104 * Note: currently doesn't work
1105 * @param text Text item which passes contents as URL for browser.
1106 * @throws Exception
1107 */
1108 public static void startBrowser(Item text) throws Exception {
1109 if (!(text instanceof Text)) {
1110 MessageBay.errorMessage("Must be a text item.");
1111 return;
1112 }
1113 if(text.getLink() != null) {
1114 MessageBay.errorMessage("Text item cannot have link.");
1115 return;
1116 }
1117
1118 Text wt = new Text("@iw:org.expeditee.items.widgets.JfxBrowser"); // create new text item for browser widget
1119
1120 if(FreeItems.textOnlyAttachedToCursor()) { // navigates to url specified by the text item
1121 wt.appendText(":" + text.getText());
1122 } else {
1123 wt.appendText(":http://www.waikato.ac.nz");
1124 }
1125
1126 FreeItems.getInstance().clear(); // remove url text from cursor
1127
1128 wt.setParent(DisplayIO.getCurrentFrame()); // set parent of text source for InteractiveWidget.createWidget()
1129 wt.setXY(FrameMouseActions.getX(), FrameMouseActions.getY());
1130 // create widget from text item
1131 JfxBrowser browser = (JfxBrowser) InteractiveWidget.createWidget(wt);
1132
1133 FrameMouseActions.pickup(browser.getItems()); // attach browser widget to mouse
1134 }
1135
1136 /**
1137 * Text item becomes link to new frame containing items.widgets.JfxBrowser and uses Text item as URL for browser.
1138 * Note: currently doesn't work
1139 * @param text Text item which passes contents as URL for browser and becomes link to the browser's new frame.
1140 * @throws Exception
1141 */
1142 public static void startBrowserNewFrame(Item text) throws Exception {
1143 if (!(text instanceof Text)) {
1144 MessageBay.errorMessage("Must be a text item.");
1145 return;
1146 }
1147 if(text.getLink() != null) { // text item can't already have a link
1148 MessageBay.errorMessage("Text item already has link.");
1149 return;
1150 }
1151
1152 // If no text with url is passed to action create a new text item with http://www.waikato.ac.nz for a default url
1153 if(!FreeItems.textOnlyAttachedToCursor()) {
1154 text = DisplayIO.getCurrentFrame().addText(FrameMouseActions.getX(), FrameMouseActions.getY(),
1155 "http://www.waikato.ac.nz", null);
1156 text.setParent(DisplayIO.getCurrentFrame()); // set parent of text source for InteractiveWidget.createWidget()
1157 FrameMouseActions.pickup(text); // Attach new text link to cursor
1158 }
1159
1160 // Create JfxBrowser widget on a new frame
1161 Frame frame = FrameIO.CreateNewFrame(text); // create new frame for browser
1162 text.setLink("" + frame.getNumber()); // link this text item to new frame
1163 // Create widget via text annotation
1164 Text wt = frame.addText(0, JfxBrowser.VERT_OFFSET, "@iw: org.expeditee.items.widgets.JfxBrowser "+ (Browser._theBrowser.getWidth() - JfxBrowser.HORZ_CROP)+
1165 " " + (Browser._theBrowser.getHeight() - JfxBrowser.VERT_CROP) + " : ".concat(text.getText()), null);
1166 InteractiveWidget.createWidget(wt);
1167
1168 FrameIO.SaveFrame(frame); // save frame to disk
1169 }
1170
1171 private static boolean startWidget(String name) throws Exception {
1172 String fullName = Actions.getClassName(name);
1173 if(fullName == null) {
1174 return false;
1175 }
1176 MessageBay.displayMessage("Creating new \"" + fullName + "\"");
1177
1178 FreeItems.getInstance().clear();
1179 Text wt = new Text("@iw:" + fullName); // create new text item for browser widget
1180 wt.setParent(DisplayIO.getCurrentFrame()); // set parent of text source for InteractiveWidget.createWidget()
1181 wt.setXY(FrameMouseActions.getX(), FrameMouseActions.getY()); // move to the mouse cursor
1182 InteractiveWidget widget = InteractiveWidget.createWidget(wt);
1183 FrameMouseActions.pickup(widget.getItems());
1184
1185 return true;
1186 }
1187
1188 private static void runUnknown(String command) throws Exception {
1189 if(startWidget(command)) {
1190 return;
1191 }
1192
1193 Actions.PerformAction(DisplayIO.getCurrentFrame(), null, command);
1194 }
1195
1196 public static void run(String command) throws Exception {
1197 if(command == null) {
1198 MessageBay.warningMessage("Please provide a command to run");
1199 return;
1200 }
1201 int firstSpace = command.indexOf(" ");
1202 if(firstSpace == -1) {
1203 runUnknown(command);
1204 return;
1205 }
1206 String argLower = command.toLowerCase();
1207 String name = argLower.substring(0, firstSpace).trim(); // first word
1208 String args = argLower.substring(firstSpace).trim(); // remainder after first word
1209 if(name == "action" || name == "agent") {
1210 if(args.length() > 0) {
1211 Actions.PerformAction(DisplayIO.getCurrentFrame(), null, args);
1212 } else {
1213 MessageBay.displayMessage("Please specify an action/agent name");
1214 }
1215 } else if(name == "widget") {
1216 if(args.length() > 0) {
1217 if(!startWidget(args)) {
1218 MessageBay.displayMessage("Widget \"" + name + "\" does not exist");
1219 }
1220 } else {
1221 MessageBay.displayMessage("Please specify a widget name");
1222 }
1223 } else {
1224 runUnknown(command);
1225 }
1226 }
1227
1228 public static void run(Item item) throws Exception {
1229 if(item == null) {
1230 MessageBay.warningMessage("Please provide a command to run");
1231 return;
1232 }
1233 run(((Text)item).getText());
1234 }
1235
1236 // Javascript stuff. Probably completely pointless since there is already a Javascript class,
1237 // but I couldn't get the existing Javascript engine to work for some reason
1238 private static ScriptEngineManager sem = new ScriptEngineManager();
1239 private static ScriptEngine se = sem.getEngineByMimeType("application/javascript");
1240
1241 private static class JSParser {
1242 private List<Frame> seen = new LinkedList<Frame>();
1243 private StringBuffer sb = new StringBuffer();
1244
1245 public JSParser(Frame frame, boolean followLinks) throws Exception {
1246 parseScript(frame, followLinks);
1247 }
1248
1249 private void parseScript(Frame frame, boolean followLinks) throws Exception {
1250
1251 if(frame == null) {
1252 return;
1253 }
1254
1255 // make sure we don't get into an infinite loop
1256 // TODO: find a smarter way to do this that allows reusing frames but still stops infinite loops?
1257 seen.add(frame);
1258
1259 // get all items on the frame
1260 List<Item> y_ordered_items = (List<Item>)frame.getItems();
1261 // remove the title item
1262 y_ordered_items.remove(frame.getTitleItem());
1263
1264 XGroupItem toplevel_xgroup = new XGroupItem(frame,y_ordered_items);
1265 // ... following on from Steps 1 and 2 in the Constructor in XGroupItem ...
1266
1267 // Step 3: Reposition any 'out-of-flow' XGroupItems
1268 toplevel_xgroup.repositionOutOfFlowGroups(toplevel_xgroup);
1269
1270 // Step 4: Now add in the remaining (nested) XGroupItems
1271 List<XGroupItem> grouped_item_list = toplevel_xgroup.getGroupedItemList();
1272 toplevel_xgroup.mapInXGroupItemsRecursive(grouped_item_list);
1273
1274 // Finally, retrieve linear list of all Items, (ordered, Y by X, allowing for overlap, nested-boxing, and arrow flow)
1275 List<Item> overlapping_y_ordered_items = toplevel_xgroup.getYXOverlappingItemList();
1276
1277 // Loop through the items looking for code and links to new frames
1278 for(Item i : overlapping_y_ordered_items) {
1279 if(followLinks && i.hasLink()) {
1280 Frame child = i.getChild();
1281 if(child != null && !seen.contains(child)) {
1282 this.parseScript(child, true);
1283 }
1284 }
1285 if(i instanceof Text && !i.isAnnotation()) {
1286 sb.append(((Text)i).getText() + "\n");
1287 }
1288 }
1289 }
1290
1291 public String getScript() {
1292 System.out.println("*****\n" + sb.toString() + "\n*****");
1293 return sb.toString();
1294 }
1295 }
1296
1297 public static void runJSFrame(Frame frame) throws Exception {
1298 se.eval(new JSParser(frame, true).getScript());
1299 }
1300
1301 public static void runJSItem(Item item) throws Exception {
1302 // if the user clicked on the action without an item on their cursor
1303 if(item.hasAction()) {
1304 for(String s : item.getAction()) {
1305 if(!s.equalsIgnoreCase("runJSItem")) {
1306 return;
1307 }
1308 }
1309 item.getParent().getNonAnnotationText(true);
1310 // Check if there is an arrow pointing from the action to a link
1311 // Seemed like a cool idea before I made it, but the arrows kind of get in the way when trying to click on the action
1312 Collection<Item> itemsWithin = item.getParentOrCurrentFrame().getItemsWithin(item.getPolygon());
1313 for(Item i : itemsWithin) {
1314 if(i instanceof Line && itemsWithin.contains(((Line)i).getStartItem())) {
1315 Polygon p = ((Line)i).getEndItem().getPolygon();
1316 for(Item j : ((Line)i).getEndItem().getParent().getAllItems()) {
1317 if(p.intersects(j.getArea().getBounds2D()) && j instanceof Text && j.hasLink()) {
1318 Frame child = j.getChild();
1319 if(child == null) {
1320 continue;
1321 }
1322 runJSFrame(child);
1323 return;
1324 }
1325 }
1326 }
1327 }
1328
1329 runJSFrame(item.getParentOrCurrentFrame());
1330 return;
1331 }
1332 Frame frame = item.getChild();
1333 if(frame == null) {
1334 frame = FrameIO.CreateNewFrame(item);
1335 item.setLink("" + frame.getNumber());
1336 frame.addText(100, 100, "print(\"test\");", null);
1337 FrameIO.SaveFrame(frame);
1338 } else {
1339 runJSFrame(frame);
1340 }
1341 }
1342
1343 public static void parsePage(Item text) throws Exception {
1344 if (!(text instanceof Text) || !FreeItems.textOnlyAttachedToCursor()) {
1345 MessageBay.errorMessage("Must provide a text item.");
1346 return;
1347 }
1348 if(text.getLink() != null) { // text item can't already have a link
1349 MessageBay.errorMessage("Text item already has link.");
1350 return;
1351 }
1352
1353 // Create JfxBrowser widget on a new frame
1354 Frame frame = FrameIO.CreateNewFrame(text); // create new frame for browser
1355 text.setLink("" + frame.getNumber()); // link this text item to new frame
1356 frame.addText(100, 100, "test", null);
1357
1358 WebParser.parseURL(text.getText(), frame);
1359 System.out.println(text.getText());
1360 }
1361
1362 public static void testWriter(Frame frame) {
1363 try {
1364 new XMLWriter().writeFrame(frame);
1365 } catch (Exception e) {
1366 e.printStackTrace();
1367 }
1368 }
1369}
Note: See TracBrowser for help on using the repository browser.