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

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

Added some math stuff... able to group numbers inside a rectangle

File size: 21.6 KB
Line 
1package org.expeditee.actions;
2
3import java.awt.Color;
4import java.awt.Image;
5import java.awt.image.BufferedImage;
6import java.awt.image.VolatileImage;
7import java.io.File;
8import java.io.FileNotFoundException;
9import java.io.IOException;
10import java.lang.reflect.Method;
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.List;
14
15import javax.imageio.ImageIO;
16
17import org.expeditee.gui.DisplayIO;
18import org.expeditee.gui.Frame;
19import org.expeditee.gui.FrameGraphics;
20import org.expeditee.gui.FrameIO;
21import org.expeditee.gui.FrameMouseActions;
22import org.expeditee.gui.MessageBay;
23import org.expeditee.gui.TimeKeeper;
24import org.expeditee.items.Item;
25import org.expeditee.items.ItemUtils;
26import org.expeditee.items.Text;
27import org.expeditee.math.ExpediteeJEP;
28import org.expeditee.stats.CometStats;
29import org.expeditee.stats.SessionStats;
30import org.expeditee.stats.StatsLogger;
31import org.expeditee.stats.TreeStats;
32import org.nfunk.jep.Node;
33import org.nfunk.jep.ParseException;
34
35/**
36 * A list of miscellaneous Actions and Actions specific to Expeditee
37 *
38 */
39public class Misc {
40 /**
41 * Causes the system to beep
42 */
43 public static void beep() {
44 java.awt.Toolkit.getDefaultToolkit().beep();
45 }
46
47 /**
48 * Forces a repaint of the current Frame
49 */
50 public static void display() {
51 FrameGraphics.refresh(false);
52 }
53
54 /**
55 * Restores the current frame to the last saved version currently on the
56 * hard disk
57 */
58 public static void restore() {
59 FrameIO.Reload();
60 // MessageBay.displayMessage("Restoration complete.");
61 }
62
63 /**
64 * Toggles AudienceMode on or off
65 */
66 public static void toggleAudienceMode() {
67 FrameGraphics.ToggleAudienceMode();
68 }
69
70 /**
71 * Toggles TwinFrames mode on or off
72 */
73 public static void toggleTwinFramesMode() {
74 DisplayIO.ToggleTwinFrames();
75 }
76
77 /**
78 * If the given Item is a Text Item, then the text of the Item is
79 * interpreted as actions, if not this method does nothing.
80 *
81 * @param current
82 * The Item to read the Actions from
83 */
84 public static void runItem(Item current) throws Exception {
85 if (current instanceof Text) {
86 List<String> actions = ((Text) current).getTextList();
87 for (String action : actions) {
88 if (!action.equalsIgnoreCase("runitem")) {
89 Actions.PerformAction(DisplayIO.getCurrentFrame(), current,
90 action);
91 }
92 }
93 } else {
94 MessageBay.errorMessage("Item must be a text item.");
95 }
96 }
97
98 /**
99 * Prompts the user to confirm deletion of the current Frame, and deletes if
100 * the user chooses. After deletion this action calls back(), to ensure the
101 * deleted frame is not still being shown
102 *
103 */
104 public static void DeleteFrame() {
105 Frame toDelete = DisplayIO.getCurrentFrame();
106 String deletedFrame = toDelete.getName();
107 String deletedFrameNameLowercase = deletedFrame.toLowerCase();
108 String errorMessage = "Error deleting " + deletedFrame;
109 try {
110 String deletedFrameName = FrameIO.DeleteFrame(toDelete);
111 if (deletedFrameName != null) {
112 DisplayIO.Back();
113 // Remove any links on the previous frame to the one being
114 // deleted
115 Frame current = DisplayIO.getCurrentFrame();
116 for (Item i : current.getItems())
117 if (i.getLink() != null
118 && i.getAbsoluteLink().toLowerCase().equals(
119 deletedFrameNameLowercase)) {
120 i.setLink(null);
121 }
122 MessageBay.displayMessage(deletedFrame + " renamed "
123 + deletedFrameName);
124 // FrameGraphics.Repaint();
125 return;
126 }
127 } catch (IOException ioe) {
128 if (ioe.getMessage() != null)
129 errorMessage += ". " + ioe.getMessage();
130 } catch (SecurityException se) {
131 if (se.getMessage() != null)
132 errorMessage += ". " + se.getMessage();
133 } catch (Exception e) {
134 e.printStackTrace();
135 }
136 MessageBay.errorMessage(errorMessage);
137 }
138
139 /**
140 * Loads the Frame linked to by the given Item. The first Item on the Frame
141 * that is not the title or name is then placed on the cursor. If the given
142 * Item has no link, or no item is found then this is a no-op.
143 *
144 * @param current
145 * The Item that links to the Frame that the Item will be loaded
146 * from.
147 */
148 public static Item GetItemFromChildFrame(Item current) {
149 return getFromChildFrame(current, false);
150 }
151
152 public static void GetItemsFromChildFrame(Item current) {
153 getItemsFromChildFrame(current, false);
154 }
155
156 /**
157 * Loads the Frame linked to by the given Item. The first Text Item on the
158 * Frame that is not the title or name is then placed on the cursor. If the
159 * given Item has no link, or no item is found then this is a no-op.
160 *
161 * @param current
162 * The Item that links to the Frame that the Item will be loaded
163 * from.
164 */
165 public static Item GetTextFromChildFrame(Item current) {
166 return getFromChildFrame(current, true);
167 }
168
169 private static Item getFromChildFrame(Item current, boolean textOnly) {
170 Item item = getFirstBodyItemOnChildFrame(current, textOnly);
171 // if no item was found
172 if (item != null) {
173 // copy the item and switch
174 item = item.copy();
175 item.setPosition(DisplayIO.getMouseX(), FrameMouseActions.getY());
176 }
177 return item;
178 }
179
180 private static void getItemsFromChildFrame(Item current, boolean textOnly) {
181 Collection<Item> items = getItemsOnChildFrame(current, textOnly);
182 // if no item was found
183 if (items == null || items.size() == 0) {
184 return;
185 }
186
187 // copy the item and switch
188 Collection<Item> copies = ItemUtils.CopyItems(items);
189 Item first = items.iterator().next();
190 float deltaX = DisplayIO.getMouseX() - first.getX();
191 float deltaY = FrameMouseActions.getY() - first.getY();
192 for (Item i : copies) {
193 if (i.isVisible())
194 i.setXY(i.getX() + deltaX, i.getY() + deltaY);
195 i.setParent(null);
196 }
197 FrameMouseActions.pickup(copies);
198 FrameGraphics.Repaint();
199 }
200
201 /**
202 * Sets the given Item to have the Given Color. Color can be null (for
203 * default)
204 *
205 * @param toChange
206 * The Item to set the Color.
207 * @param toUse
208 * The Color to give the Item.
209 */
210 public static void SetItemBackgroundColor(Item toChange, Color toUse) {
211 if (toChange == null)
212 return;
213
214 toChange.setBackgroundColor(toUse);
215 FrameGraphics.Repaint();
216 }
217
218 /**
219 * Sets the given Item to have the Given Color. Color can be null (for
220 * default)
221 *
222 * @param toChange
223 * The Item to set the Color.
224 * @param toUse
225 * The Color to give the Item.
226 */
227 public static void SetItemColor(Item toChange, Color toUse) {
228 if (toChange == null)
229 return;
230
231 toChange.setColor(toUse);
232 FrameGraphics.Repaint();
233 }
234
235 /**
236 * Creates a new Text Object containing general statistics for the current
237 * session. The newly created Text Object is then attached to the cursor via
238 * FrameMouseActions.pickup(Item)
239 */
240 public static void GetSessionStats() {
241 attachStatsToCursor(SessionStats.getCurrentStats());
242 }
243
244 /**
245 * Creates a new Text Object containing statistics for the current tree.
246 */
247 public static String GetCometStats(Frame frame) {
248 TimeKeeper timer = new TimeKeeper();
249 MessageBay.displayMessage("Computing comet stats...");
250 CometStats cometStats = new CometStats(frame);
251 String result = cometStats.toString();
252 MessageBay.overwriteMessage("Comet stats time: "
253 + timer.getElapsedStringSeconds());
254 return result;
255 }
256
257 public static String GetTreeStats(Frame frame) {
258 TimeKeeper timer = new TimeKeeper();
259 MessageBay.displayMessage("Computing tree stats...");
260
261 TreeStats treeStats = new TreeStats(frame);
262 String result = treeStats.toString();
263 MessageBay.overwriteMessage("Tree stats time: "
264 + timer.getElapsedStringSeconds());
265 return result;
266
267 }
268
269 /**
270 * Creates a text item and attaches it to the cursor.
271 *
272 * @param itemText
273 * the text to attach to the cursor
274 */
275 public static void attachStatsToCursor(String itemText) {
276 SessionStats.CreatedText();
277 Frame current = DisplayIO.getCurrentFrame();
278 Item text = current.getStatsTextItem(itemText);
279 FrameMouseActions.pickup(text);
280 FrameGraphics.Repaint();
281 }
282
283 public static void attachTextToCursor(String itemText) {
284 SessionStats.CreatedText();
285 Frame current = DisplayIO.getCurrentFrame();
286 Item text = current.getTextItem(itemText);
287 FrameMouseActions.pickup(text);
288 FrameGraphics.Repaint();
289 }
290
291 /**
292 * Creates a new Text Object containing statistics for moving, deleting and
293 * creating items in the current session. The newly created Text Object is
294 * then attached to the cursor via FrameMouseActions.pickup(Item)
295 */
296 public static String getItemStats() {
297 return SessionStats.getItemStats();
298 }
299
300 /**
301 * Creates a new Text Object containing statistics for the time between
302 * events triggered by the user through mouse clicks and key presses. The
303 * newly created Text Object is then attached to the cursor via
304 * FrameMouseActions.pickup(Item)
305 */
306 public static String getEventStats() {
307 return SessionStats.getEventStats();
308 }
309
310 /**
311 * Creates a new Text Object containing the contents of the current frames
312 * file.
313 */
314 public static String getFrameFile(Frame frame) {
315 return FrameIO.ForceSaveFrame(frame);
316 }
317
318 /**
319 * Creates a new Text Object containing the available fonts.
320 */
321 public static String getFontNames() {
322 Collection<String> availableFonts = Actions.getFonts().values();
323 StringBuilder fontsList = new StringBuilder();
324 for (String s : availableFonts) {
325 fontsList.append(s).append(Text.LINE_SEPARATOR);
326 }
327 fontsList.deleteCharAt(fontsList.length() - 1);
328
329 return fontsList.toString();
330 }
331
332 public static String getUnicodeCharacters(int start, int finish) {
333 if (start < 0 && finish < 0) {
334 throw new RuntimeException("Parameters must be non negative");
335 }
336 // Swap the start and finish if they are inthe wrong order
337 if (start > finish) {
338 start += finish;
339 finish = start - finish;
340 start = start - finish;
341 }
342 StringBuilder charList = new StringBuilder();
343 int count = 0;
344 charList.append(String.format("Unicode block 0x%x - 0x%x", start,
345 finish));
346 System.out.println();
347 // charList.append("Unicode block: ").append(String.format(format,
348 // args))
349 for (char i = (char) start; i < (char) finish; i++) {
350 if (Character.isDefined(i)) {
351 if (count++ % 64 == 0)
352 charList.append(Text.LINE_SEPARATOR);
353 charList.append(Character.valueOf(i));
354 }
355 }
356 return charList.toString();
357 }
358
359 /**
360 * Gets a single block of Unicode characters.
361 *
362 * @param start
363 * the start of the block
364 */
365 public static String getUnicodeCharacters(int start) {
366 return getUnicodeCharacters(start, start + 256);
367 }
368
369 public static String getMathSymbols() {
370 return getUnicodeCharacters('\u2200', '\u2300');
371 }
372
373 /**
374 * Resets the statistics back to zero.
375 */
376 public static void repaint() {
377 StatsLogger.WriteStatsFile();
378 SessionStats.resetStats();
379 }
380
381 /**
382 * Loads a frame with the given name and saves it as a JPEG image.
383 *
384 * @param framename
385 * The name of the Frame to save
386 */
387 public static void jpegFrame(String framename) {
388 ImageFrame(framename, "JPEG");
389 }
390
391 /**
392 * Saves the current frame as a JPEG image. This is the same as calling
393 * JpegFrame(currentFrame.getName())
394 */
395 public static void jpegFrame() {
396 ImageFrame(DisplayIO.getCurrentFrame().getName(), "JPEG");
397 }
398
399 public static void jpgFrame() {
400 jpegFrame();
401 }
402
403 /**
404 * Loads a frame with the given name and saves it as a PNG image.
405 *
406 * @param framename
407 * The name of the Frame to save
408 */
409 public static void PNGFrame(String framename) {
410 ImageFrame(framename, "PNG");
411 }
412
413 /**
414 * Saves the current frame as a PNG image. This is the same as calling
415 * PNGFrame(currentFrame.getName())
416 */
417 public static void PNGFrame() {
418 ImageFrame(DisplayIO.getCurrentFrame().getName(), "PNG");
419 }
420
421 public static String SaveImage(BufferedImage screen, String format,
422 String directory, String fileName) {
423 // Check if we need to append the suffix
424 if (fileName.indexOf('.') < 0)
425 fileName += "." + format.toLowerCase();
426
427 try {
428 // set up the file for output
429 String fullFileName = directory + fileName;
430 File out = new File(fullFileName);
431 if (!out.getParentFile().exists())
432 out.mkdirs();
433
434 // If the image is successfully written out return the fileName
435 if (ImageIO.write(screen, format, out))
436 return fileName;
437
438 } catch (Exception e) {
439 e.printStackTrace();
440 }
441 return null;
442 }
443
444 public static String ImageFrame(Frame frame, String format, String directory) {
445 assert (frame != null);
446
447 Image oldBuffer = frame.getBuffer();
448 frame.setBuffer(null);
449 // Jpeg only works properly with volitile frames
450 // Png transparency only works with bufferedImage form
451 Image frameBuffer = FrameGraphics.getBuffer(frame, false, format
452 .equalsIgnoreCase("jpeg"));
453 // Make sure overlay stuff doesnt disapear on the frame visible on the
454 // screen
455 frame.setBuffer(oldBuffer);
456 BufferedImage screen = null;
457
458 if (frameBuffer instanceof VolatileImage) {
459 // If its the current frame it will be a volitive image
460 screen = ((VolatileImage) frameBuffer).getSnapshot();
461 } else {
462 assert (frameBuffer instanceof BufferedImage);
463 screen = (BufferedImage) frameBuffer;
464 }
465 return SaveImage(screen, format, directory, frame.getExportFileName());
466 }
467
468 /**
469 * Saves the Frame with the given Framename as an image of the given format.
470 *
471 * @param framename
472 * The name of the Frame to save as an image
473 * @param format
474 * The Image format to use (i.e. "PNG", "BMP", etc)
475 */
476 public static void ImageFrame(String framename, String format) {
477 Frame loaded = FrameIO.LoadFrame(framename);
478
479 // if the frame was loaded successfully
480 if (loaded != null) {
481 String path = FrameIO.IMAGES_PATH;
482 String frameName = ImageFrame(loaded, format, path);
483 if (frameName != null)
484 MessageBay.displayMessage("Frame successfully saved to " + path
485 + frameName);
486 else
487 MessageBay.errorMessage("Could not find image writer for "
488 + format + " format");
489 // if the frame was not loaded successfully, alert the user
490 } else {
491 MessageBay.displayMessage("Frame '" + framename
492 + "' could not be found.");
493 }
494 }
495
496 /**
497 * Displays a message in the message box area.
498 *
499 * @param message
500 * the message to display
501 */
502 public static void MessageLn(String message) {
503 MessageBay.displayMessage(message);
504 }
505
506 public static void MessageLn2(String message, String message2) {
507 MessageBay.displayMessage(message + " " + message2);
508 }
509
510 public static void CopyFile(String existingFile, String newFileName) {
511 try {
512 // TODO is there a built in method which will do this faster?
513
514 MessageBay.displayMessage("Copying file " + existingFile + " to "
515 + newFileName + "...");
516 FrameIO.copyFile(existingFile, newFileName);
517 MessageBay.displayMessage("File copied successfully");
518 } catch (FileNotFoundException e) {
519 MessageBay.displayMessage("Error opening file: " + existingFile);
520 } catch (Exception e) {
521 MessageBay.displayMessage("File could not be copied");
522 }
523 }
524
525 /**
526 * Runs two methods alternatively a specified number of times and reports on
527 * the time spent running each method.
528 *
529 * @param fullMethodNameA
530 * @param fullMethodNameB
531 * @param repsPerTest
532 * the number of time each method is run per test
533 * @param tests
534 * the number of tests to conduct
535 *
536 */
537 public static void CompareMethods(String fullMethodNameA,
538 String fullMethodNameB, int repsPerTest, int tests) {
539 try {
540 String classNameA = getClassName(fullMethodNameA);
541 String classNameB = getClassName(fullMethodNameB);
542 String methodNameA = getMethodName(fullMethodNameA);
543 String methodNameB = getMethodName(fullMethodNameB);
544
545 Class<?> classA = Class.forName(classNameA);
546 Class<?> classB = Class.forName(classNameB);
547 Method methodA = classA.getDeclaredMethod(methodNameA,
548 new Class[] {});
549 Method methodB = classB.getDeclaredMethod(methodNameB,
550 new Class[] {});
551 TimeKeeper timeKeeper = new TimeKeeper();
552 long timeA = 0;
553 long timeB = 0;
554 // Run the tests
555 for (int i = 0; i < tests; i++) {
556 // Test methodA
557 timeKeeper.restart();
558 for (int j = 0; j < repsPerTest; j++) {
559 methodA.invoke((Object) null, new Object[] {});
560 }
561 timeA += timeKeeper.getElapsedMillis();
562 timeKeeper.restart();
563 // Test methodB
564 for (int j = 0; j < repsPerTest; j++) {
565 methodB.invoke((Object) null, new Object[] {});
566 }
567 timeB += timeKeeper.getElapsedMillis();
568 }
569
570 float aveTimeA = timeA * 1000F / repsPerTest / tests;
571 float aveTimeB = timeB * 1000F / repsPerTest / tests;
572 // Display Results
573 MessageBay.displayMessage("Average Execution Time");
574 MessageBay.displayMessage(methodNameA + ": "
575 + TimeKeeper.Formatter.format(aveTimeA) + "us");
576 MessageBay.displayMessage(methodNameB + ": "
577 + TimeKeeper.Formatter.format(aveTimeB) + "us");
578 } catch (Exception e) {
579 MessageBay.errorMessage(e.getClass().getSimpleName() + ": "
580 + e.getMessage());
581 }
582 }
583
584 public static String getClassName(String fullMethodName) {
585 assert (fullMethodName != null);
586 assert (fullMethodName.length() > 0);
587 int lastPeriod = fullMethodName.lastIndexOf('.');
588 if (lastPeriod > 0 && lastPeriod < fullMethodName.length() - 1)
589 return fullMethodName.substring(0, lastPeriod);
590 throw new RuntimeException("Invalid method name: " + fullMethodName);
591 }
592
593 public static String getMethodName(String methodName) {
594 assert (methodName != null);
595 assert (methodName.length() > 0);
596 int lastPeriod = methodName.lastIndexOf('.');
597 if (lastPeriod > 0 && lastPeriod < methodName.length() - 1)
598 return methodName.substring(1 + lastPeriod);
599 throw new RuntimeException("Invalid method name: " + methodName);
600 }
601
602 /**
603 * Loads the Frame linked to by the given Item. The first Item on the Frame
604 * that is not the title or name is then placed on the current frame. The
605 * item that was clicked on is placed on the frame it was linked to and the
606 * link is switched to the item from the child frame. If the given Item has
607 * no link, or no item is found then this is a no-op.
608 *
609 * @param current
610 * The Item that links to the Frame that the Item will be loaded
611 * from.
612 */
613 public static void SwapItemWithItemOnChildFrame(Item current) {
614 Item item = getFirstBodyItemOnChildFrame(current, false);
615 // if no item was found
616 if (item == null) {
617 return;
618 }
619
620 // swap the items parents
621 Frame parentFrame = current.getParent();
622 Frame childFrame = item.getParent();
623 current.setParent(childFrame);
624 item.setParent(parentFrame);
625
626 // swap the items on the frames
627 parentFrame.removeItem(current);
628 childFrame.removeItem(item);
629 parentFrame.addItem(item);
630 childFrame.addItem(current);
631
632 // swap the items links
633 item.setActions(current.getAction());
634 item.setLink(childFrame.getName());
635 current.setLink(parentFrame.getName());
636 // current.setLink(null);
637 current.setActions(null);
638
639 FrameGraphics.Repaint();
640 }
641
642 private static Item getFirstBodyItemOnChildFrame(Item current,
643 boolean textOnly) {
644 // the item must link to a frame
645 if (current.getLink() == null) {
646 MessageBay
647 .displayMessage("Cannot get item from child - this item has no link");
648 return null;
649 }
650
651 Frame child = FrameIO.LoadFrame(current.getAbsoluteLink());
652
653 // if the frame could not be loaded
654 if (child == null) {
655 MessageBay.errorMessage("Could not load child frame.");
656 return null;
657 }
658
659 // find the first non-title and non-name item
660 List<Item> body = new ArrayList<Item>();
661 if (textOnly)
662 body.addAll(child.getBodyTextItems(false));
663 else
664 body.addAll(child.getItems());
665 Item item = null;
666
667 for (Item i : body)
668 if (i != child.getTitleItem() && !i.isAnnotation()) {
669 item = i;
670 break;
671 }
672
673 // if no item was found
674 if (item == null) {
675 MessageBay.displayMessage("No item found to copy");
676 return null;
677 }
678
679 return item;
680 }
681
682 private static Collection<Item> getItemsOnChildFrame(Item current,
683 boolean textOnly) {
684 // the item must link to a frame
685 if (current.getLink() == null) {
686 MessageBay
687 .displayMessage("Cannot get item from child - this item has no link");
688 return null;
689 }
690 Frame child = FrameIO.LoadFrame(current.getAbsoluteLink());
691
692 // if the frame could not be loaded
693 if (child == null) {
694 MessageBay.errorMessage("Could not load child frame.");
695 return null;
696 }
697
698 // find the first non-title and non-name item
699 Collection<Item> body = new ArrayList<Item>();
700 if (textOnly)
701 body.addAll(child.getBodyTextItems(false));
702 else
703 body.addAll(child.getItems());
704
705 return body;
706 }
707
708 public static void calculate(Frame frame, Item toCalculate) {
709 if (toCalculate instanceof Text) {
710 Text text = (Text) toCalculate;
711 ExpediteeJEP myParser = new ExpediteeJEP();
712 myParser.addVariables(frame);
713 String linkedFrame = toCalculate.getAbsoluteLink();
714 if (linkedFrame != null) {
715 myParser.addVariables(FrameIO.LoadFrame(linkedFrame));
716 }
717 myParser.resetObserver();
718
719 // Do the calculation
720 String formulaFullCase = text.getText().replace('\n', ' ');
721 String formula = formulaFullCase.toLowerCase();
722
723 try {
724 Node node = myParser.parse(formula);
725 Object result = myParser.evaluate(node);
726 text.setText(result.toString());
727 text.setFormula(formulaFullCase);
728 if (text.isFloating()) {
729 text.setPosition(FrameMouseActions.MouseX,
730 FrameMouseActions.MouseY);
731 FrameMouseActions.resetOffset();
732 } else {
733 text.getParentOrCurrentFrame().change();
734 }
735 } catch (ParseException e) {
736 MessageBay.errorMessage("Parse error "
737 + e.getMessage().replace("\n", ""));
738 } catch (Exception e) {
739 MessageBay.errorMessage("evaluation error "
740 + e.getMessage().replace("\n", ""));
741 e.printStackTrace();
742 }
743 }
744 }
745
746 /**
747 * Attach an item to the cursor.
748 *
749 * @param item
750 */
751 public static void attachToCursor(Item item) {
752 item.setParent(null);
753 FrameMouseActions.pickup(item);
754 FrameGraphics.Repaint();
755 }
756}
Note: See TracBrowser for help on using the repository browser.