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

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

Add support for following links to JS parsing action, and make sure it won't get into an infinite loop of following links.
TODO: find a better way of avoiding infinite loops (one that allows multiple use of frames, but still stops infinite loops - look at how arrow following works)

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