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

Last change on this file since 1468 was 1468, checked in by bnemhaus, 4 years ago

Add a key character

File size: 48.1 KB
Line 
1/**
2 * Misc.java
3 * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package org.expeditee.actions;
20
21import java.io.BufferedReader;
22import java.io.File;
23import java.io.FileNotFoundException;
24import java.io.IOException;
25import java.io.InputStream;
26import java.io.InputStreamReader;
27import java.lang.reflect.Method;
28import java.net.URL;
29import java.net.URLClassLoader;
30import java.nio.file.Path;
31import java.nio.file.Paths;
32import java.util.ArrayList;
33import java.util.Collection;
34import java.util.HashMap;
35import java.util.LinkedList;
36import java.util.List;
37import java.util.Map;
38import java.util.Map.Entry;
39import java.util.jar.Attributes;
40import java.util.jar.JarFile;
41
42import org.expeditee.core.Colour;
43import org.expeditee.core.Image;
44import org.expeditee.core.Point;
45import org.expeditee.gio.EcosystemManager;
46import org.expeditee.gio.gesture.StandardGestureActions;
47import org.expeditee.gui.AttributeUtils;
48import org.expeditee.gui.DisplayController;
49import org.expeditee.gui.Frame;
50import org.expeditee.gui.FrameGraphics;
51import org.expeditee.gui.FrameIO;
52import org.expeditee.gui.FrameUtils;
53import org.expeditee.gui.FreeItems;
54import org.expeditee.gui.MessageBay;
55import org.expeditee.gui.MessageBay.Progress;
56import org.expeditee.gui.Reminders;
57import org.expeditee.gui.TimeKeeper;
58import org.expeditee.io.Conversion;
59import org.expeditee.io.ExpReader;
60import org.expeditee.items.Item;
61import org.expeditee.items.ItemUtils;
62import org.expeditee.items.Line;
63import org.expeditee.items.Picture;
64import org.expeditee.items.Text;
65import org.expeditee.items.XRayable;
66import org.expeditee.items.widgets.Widget;
67import org.expeditee.items.widgets.WidgetCorner;
68import org.expeditee.items.widgets.WidgetEdge;
69import org.expeditee.math.ExpediteeJEP;
70import org.expeditee.settings.UserSettings;
71import org.expeditee.simple.SString;
72import org.expeditee.stats.CometStats;
73import org.expeditee.stats.DocumentStatsFast;
74import org.expeditee.stats.SessionStats;
75import org.expeditee.stats.StatsLogger;
76import org.expeditee.stats.TreeStats;
77import org.nfunk.jep.Node;
78import org.nfunk.jep.ParseException;
79
80
81
82/**
83 * A list of miscellaneous Actions specific to Expeditee
84 *
85 */
86public class Misc {
87
88
89 public static String keyCharacter() {
90 return "\uD83D\uDD11";
91 }
92
93 /**
94 * Causes the system to beep
95 */
96 public static void beep()
97 {
98 EcosystemManager.getMiscManager().beep();
99 }
100
101 /**
102 * Returns an Item located at the specified position.
103 * kgas1 - 23/01/2012
104 * @param x
105 * @param y
106 * @return
107 */
108 public static Item getItemAtPosition(int x, int y, Frame f)
109 {
110 Frame current = f;
111 List<Item> allItems = current.getSortedItems();
112
113 for(Item i : allItems)
114 {
115 if(i.getX() == x && i.getY() == y)
116 return i;
117 }
118
119 return null;
120 }
121
122 /**
123 * Returns an item containing a specified piece of data.
124 * kgas1 - 7/06/2012
125 * @param s
126 * @param f
127 * @return
128 */
129 public static Item getItemContainingData(String s, Frame f){
130
131 Frame current = f;
132
133 List<Item> allItems = current.getSortedItems();
134
135
136 for(Item i : allItems){
137
138
139 if(i.getData() != null && i.getData().size() > 0){
140 if(i.getData().contains(s)){
141 return i;
142 }
143 }
144 }
145
146 return null;
147 }
148
149 public static void openURL(Item item){
150
151 if(item.getData() != null && item.getData().size() > 0){
152
153 String url = item.getData().get(0);
154 openURL(url);
155 }
156
157 //openURL("http://www.google.com");
158 }
159 /**
160 * Treats a string as a URL and attempts to open it
161 */
162 public static void openURL(String siteURL)
163 {
164 boolean success = EcosystemManager.getMiscManager().browse(siteURL);
165
166 if (!success) MessageBay.displayMessage("'" + siteURL + "' is not a valid URL");
167 }
168 /**
169 * Backs up the current frame as a default and saves it to the file
170 */
171 public static void setRestorePoint()
172 {
173 Frame current = DisplayController.getCurrentFrame();
174 current.change();
175 FrameIO.SaveFrameAsRestore(current, true, true);
176 }
177 /**
178 * Forces a repaint of the current Frame
179 */
180 public static void display() {
181 DisplayController.requestRefresh(false);
182 }
183
184 public static String getWindowSize() {
185 return EcosystemManager.getGraphicsManager().getWindowSize().toString();
186 }
187
188 /**
189 * Restores the current frame to the last saved version currently on the
190 * hard disk
191 */
192 public static void restore() {
193 FrameIO.Reload();
194 // MessageBay.displayMessage("Restoration complete.");
195 }
196
197 /**
198 * Toggles AudienceMode on or off
199 */
200 public static void toggleAudienceMode() {
201 DisplayController.ToggleAudienceMode();
202 }
203
204 /**
205 * Toggles TwinFrames mode on or off
206 */
207 public static void toggleTwinFramesMode() {
208 DisplayController.toggleTwinFrames();
209 }
210
211 /**
212 * If the given Item is a Text Item, then the text of the Item is
213 * interpreted as actions, if not this method does nothing.
214 *
215 * @param current
216 * The Item to read the Actions from
217 */
218 public static void runItem(Item current) throws Exception {
219 if (current instanceof Text) {
220 List<String> actions = ((Text) current).getTextList();
221 for (String action : actions) {
222 if (!action.equalsIgnoreCase("runitem")) {
223 Actions.LegacyPerformAction(DisplayController.getCurrentFrame(), current,
224 action);
225 }
226 }
227 } else {
228 MessageBay.errorMessage("Item must be a text item.");
229 }
230 }
231
232 /**
233 * Prompts the user to confirm deletion of the current Frame, and deletes if
234 * the user chooses. After deletion this action calls back(), to ensure the
235 * deleted frame is not still being shown
236 *
237 */
238 public static void DeleteFrame(Frame toDelete) {
239 String deletedFrame = toDelete.getName();
240 String deletedFrameNameLowercase = deletedFrame.toLowerCase();
241 String errorMessage = "Error deleting " + deletedFrame;
242 try {
243 String deletedFrameName = FrameIO.DeleteFrame(toDelete);
244 if (deletedFrameName != null) {
245 DisplayController.Back();
246 // Remove any links on the previous frame to the one being
247 // deleted
248 Frame current = DisplayController.getCurrentFrame();
249 for (Item i : current.getSortedItems())
250 if (i.getLink() != null
251 && i.getAbsoluteLink().toLowerCase().equals(
252 deletedFrameNameLowercase)) {
253 i.setLink(null);
254 }
255 MessageBay.displayMessage(deletedFrame + " renamed "
256 + deletedFrameName);
257 // FrameGraphics.Repaint();
258 return;
259 }
260 } catch (IOException ioe) {
261 if (ioe.getMessage() != null)
262 errorMessage += ". " + ioe.getMessage();
263 } catch (SecurityException se) {
264 if (se.getMessage() != null)
265 errorMessage += ". " + se.getMessage();
266 } catch (Exception e) {
267 e.printStackTrace();
268 }
269 MessageBay.errorMessage(errorMessage);
270 }
271
272 /**
273 * Loads the Frame linked to by the given Item. The first Item on the Frame
274 * that is not the title or name is then placed on the cursor. If the given
275 * Item has no link, or no item is found then this is a no-op.
276 *
277 * @param current
278 * The Item that links to the Frame that the Item will be loaded
279 * from.
280 */
281 public static Item GetItemFromChildFrame(Item current) {
282 return getFromChildFrame(current, false);
283 }
284
285 public static void GetItemsFromChildFrame(Item current) {
286 getItemsFromChildFrame(current, false);
287 }
288
289 /**
290 * Loads the Frame linked to by the given Item. The first Text Item on the
291 * Frame that is not the title or name is then placed on the cursor. If the
292 * given Item has no link, or no item is found then this is a no-op.
293 *
294 * @param current
295 * The Item that links to the Frame that the Item will be loaded
296 * from.
297 */
298 public static Item GetTextFromChildFrame(Item current) {
299 return getFromChildFrame(current, true);
300 }
301
302 private static Item getFromChildFrame(Item current, boolean textOnly) {
303 Item item = getFirstBodyItemOnChildFrame(current, textOnly);
304 // if no item was found
305 if (item != null) {
306 // copy the item and switch
307 item = item.copy();
308 item.setPosition(DisplayController.getMousePosition());
309 }
310 return item;
311 }
312
313 private static void getItemsFromChildFrame(Item current, boolean textOnly) {
314 Collection<Item> items = getItemsOnChildFrame(current, textOnly);
315 // if no item was found
316 if (items == null || items.size() == 0) {
317 return;
318 }
319
320 // copy the item and switch
321 Collection<Item> copies = ItemUtils.CopyItems(items);
322 Item first = items.iterator().next();
323 float deltaX = DisplayController.getMouseX() - first.getX();
324 float deltaY = DisplayController.getMouseY() - first.getY();
325 for (Item i : copies) {
326 if (i.isVisible())
327 i.setXY(i.getX() + deltaX, i.getY() + deltaY);
328 i.setParent(null);
329 }
330 StandardGestureActions.pickup(copies);
331 DisplayController.requestRefresh(true);
332 }
333
334 /**
335 * Sets the given Item to have the Given Color. Color can be null (for
336 * default)
337 *
338 * @param toChange
339 * The Item to set the Color.
340 * @param toUse
341 * The Color to give the Item.
342 */
343 public static void SetItemBackgroundColor(Item toChange, Colour toUse) {
344 if (toChange == null)
345 return;
346
347 toChange.setBackgroundColor(toUse);
348 DisplayController.requestRefresh(true);
349 }
350
351 /**
352 * Sets the given Item to have the Given Color. Color can be null (for
353 * default)
354 *
355 * @param toChange
356 * The Item to set the Color.
357 * @param toUse
358 * The Color to give the Item.
359 */
360 public static void SetItemColor(Item toChange, Colour toUse) {
361 if (toChange == null)
362 return;
363
364 toChange.setColor(toUse);
365 DisplayController.requestRefresh(true);
366 }
367
368 /**
369 * Creates a new Text Object containing general statistics for the current
370 * session. The newly created Text Object is then attached to the cursor via
371 * FrameMouseActions.pickup(Item)
372 */
373 public static void GetSessionStats() {
374 attachStatsToCursor(SessionStats.getCurrentStats());
375 }
376
377 /**
378 * Creates a new Text Object containing statistics for the current tree.
379 */
380 public static String GetCometStats(Frame frame) {
381 TimeKeeper timer = new TimeKeeper();
382 MessageBay.displayMessage("Computing comet stats...");
383 CometStats cometStats = new CometStats(frame);
384 String result = cometStats.toString();
385 MessageBay.overwriteMessage("Comet stats time: "
386 + timer.getElapsedStringSeconds());
387 return result;
388 }
389
390 public static String GetTreeStats(Frame frame) {
391 TimeKeeper timer = new TimeKeeper();
392 MessageBay.displayMessage("Computing tree stats...");
393
394 TreeStats treeStats = new TreeStats(frame);
395 String result = treeStats.toString();
396 MessageBay.overwriteMessage("Tree stats time: "
397 + timer.getElapsedStringSeconds());
398 return result;
399
400 }
401
402 public static String GetDocumentStats(Frame frame) {
403 TimeKeeper timer = new TimeKeeper();
404 MessageBay.displayMessage("Computing document stats...");
405 FrameIO.ForceSaveFrame(frame);
406 DocumentStatsFast docStats = new DocumentStatsFast(frame.getName(),
407 frame.getTitle());
408 String result = docStats.toString();
409
410 MessageBay.overwriteMessage("Document stats time: "
411 + timer.getElapsedStringSeconds());
412 return result;
413
414 }
415
416 /**
417 * Creates a text item and attaches it to the cursor.
418 *
419 * @param itemText
420 * the text to attach to the cursor
421 */
422 public static void attachStatsToCursor(String itemText) {
423 SessionStats.CreatedText();
424 Frame current = DisplayController.getCurrentFrame();
425 Item text = current.getStatsTextItem(itemText);
426 StandardGestureActions.pickup(text);
427 DisplayController.requestRefresh(true);
428 }
429
430 public static void attachTextToCursor(String itemText) {
431 SessionStats.CreatedText();
432 Frame current = DisplayController.getCurrentFrame();
433 Item text = current.getTextItem(itemText);
434 StandardGestureActions.pickup(text);
435 DisplayController.requestRefresh(true);
436 }
437
438 /**
439 * Creates a new Text Object containing statistics for moving, deleting and
440 * creating items in the current session. The newly created Text Object is
441 * then attached to the cursor via FrameMouseActions.pickup(Item)
442 */
443 public static String getItemStats() {
444 return SessionStats.getItemStats();
445 }
446
447 /**
448 * Creates a new Text Object containing statistics for the time between
449 * events triggered by the user through mouse clicks and key presses. The
450 * newly created Text Object is then attached to the cursor via
451 * FrameMouseActions.pickup(Item)
452 */
453 public static String getEventStats() {
454 return SessionStats.getEventStats();
455 }
456
457 /**
458 * Creates a new Text Object containing the contents of the current frames
459 * file.
460 */
461 public static String getFrameFile(Frame frame) {
462 return FrameIO.ForceSaveFrame(frame);
463 }
464
465 /**
466 * Creates a new Text Object containing the available fonts.
467 */
468 public static String getFontNames() {
469 Collection<String> availableFonts = Actions.getFonts().values();
470 StringBuilder fontsList = new StringBuilder();
471 for (String s : availableFonts) {
472 fontsList.append(s).append(Text.LINE_SEPARATOR);
473 }
474 fontsList.deleteCharAt(fontsList.length() - 1);
475
476 return fontsList.toString();
477 }
478 /**
479 * Creates a new Text Object containing the available fonts already loaded into Expeditee.
480 */
481 public static String getExpediteeFontNames(){
482
483 StringBuilder fontsList = new StringBuilder();
484
485 for (String s: Text.FONT_WHEEL){
486
487 fontsList.append(s).append(Text.LINE_SEPARATOR);
488 }
489
490 for (Entry<String, org.expeditee.core.Font> entry: Text.FONT_WHEEL_ADDITIONAL_LOOKUP.entrySet()){
491
492 String fontName = entry.getKey();
493 fontsList.append(fontName).append(Text.LINE_SEPARATOR);
494 }
495 //add the default soon too
496 fontsList.deleteCharAt(fontsList.length() - 1);
497 return fontsList.toString();
498 }
499
500 public static String getUnicodeCharacters(int start, int finish) {
501 if (start < 0 && finish < 0) {
502 throw new RuntimeException("Parameters must be non negative");
503 }
504 // Swap the start and finish if they are inthe wrong order
505 if (start > finish) {
506 start += finish;
507 finish = start - finish;
508 start = start - finish;
509 }
510 StringBuilder charList = new StringBuilder();
511 int count = 0;
512 charList.append(String.format("Unicode block 0x%x - 0x%x", start,
513 finish));
514 System.out.println(); // Is this println needed?
515 // charList.append("Unicode block: ").append(String.format(format,
516 // args))
517 for (char i = (char) start; i < (char) finish; i++) {
518 if (Character.isDefined(i)) {
519 if (count++ % 64 == 0)
520 charList.append(Text.LINE_SEPARATOR);
521 charList.append(Character.valueOf(i));
522 }
523 }
524 return charList.toString();
525 }
526
527 /**
528 * Gets a single block of Unicode characters.
529 *
530 * @param start
531 * the start of the block
532 */
533 public static String getUnicodeCharacters(int start) {
534 return getUnicodeCharacters(start, start + 256);
535 }
536
537 /**
538 * Get a single Unicode character
539 *
540 * @param codePoint
541 * the Unicode codePoint
542 */
543 public static String getUnicodeCharacter(int codePoint) {
544 char codePointChar = (char) codePoint;
545 if (Character.isDefined(codePointChar)) {
546 return Character.valueOf(codePointChar).toString();
547 }
548 else {
549 MessageBay.errorMessage("Character value '" + codePoint +"' not defined");
550 return "";
551 }
552 }
553
554 public static String getMathSymbols() {
555 return getUnicodeCharacters('\u2200', '\u2300');
556 }
557
558 /**
559 * Resets the statistics back to zero.
560 */
561 public static void repaint() {
562 StatsLogger.WriteStatsFile();
563 SessionStats.resetStats();
564 }
565
566 /**
567 * Loads a frame with the given name and saves it as a JPEG image.
568 *
569 * @param framename
570 * The name of the Frame to save
571 */
572 public static void jpegFrame(String framename) {
573 ImageFrame(framename, "JPEG");
574 }
575
576 /**
577 * Saves the current frame as a JPEG image. This is the same as calling
578 * JpegFrame(currentFrame.getName())
579 */
580 public static void jpegFrame() {
581 ImageFrame(DisplayController.getCurrentFrame().getName(), "JPEG");
582 }
583
584 public static void jpgFrame() {
585 jpegFrame();
586 }
587
588 /**
589 * Loads a frame with the given name and saves it as a PNG image.
590 *
591 * @param framename
592 * The name of the Frame to save
593 */
594 public static void PNGFrame(String framename) {
595 ImageFrame(framename, "PNG");
596 }
597
598 /**
599 * Saves the current frame as a PNG image. This is the same as calling
600 * PNGFrame(currentFrame.getName())
601 */
602 public static void PNGFrame(Frame frame) {
603 ImageFrame(frame.getName(), "PNG");
604 }
605
606 public static String SaveImage(Image screen, String format,
607 String directory, String fileName) {
608 String suffix = "." + format.toLowerCase();
609 String shortFileName = fileName;
610 // Check if we need to append the suffix
611 if (fileName.indexOf('.') < 0)
612 fileName += suffix;
613 else
614 shortFileName = fileName.substring(0, fileName.length() - suffix.length());
615
616 try {
617 int count = 2;
618 // set up the file for output
619 File out = new File(directory + fileName);
620 while (out.exists()) {
621 fileName = shortFileName + "_" + count++ + suffix;
622 out = new File(directory + fileName);
623 }
624
625 if (!out.getParentFile().exists())
626 out.mkdirs();
627
628 // If the image is successfully written out return the fileName
629 if (screen.writeToDisk(format, out))
630 return fileName;
631
632 } catch (Exception e) {
633 e.printStackTrace();
634 }
635 return null;
636 }
637
638 public static String ImageFrame(Frame frame, String format, String directory) {
639 assert (frame != null);
640
641 Image oldBuffer = frame.getBuffer();
642 frame.setBuffer(null);
643 // Jpeg only works properly with volatile frames
644 // Png transparency only works with bufferedImage form
645 Image frameBuffer = FrameGraphics.getFrameImage(frame, null, null, false, format.equalsIgnoreCase("jpeg"));
646 // Make sure overlay stuff doesnt disappear on the frame visible on the
647 // screen
648 frame.setBuffer(oldBuffer);
649
650 return SaveImage(frameBuffer, format, directory, frame.getExportFileName());
651 }
652
653 /**
654 * Saves the Frame with the given Framename as an image of the given format.
655 *
656 * @param framename
657 * The name of the Frame to save as an image
658 * @param format
659 * The Image format to use (i.e. "PNG", "BMP", etc)
660 */
661 public static void ImageFrame(String framename, String format) {
662 Frame loaded = FrameIO.LoadFrame(framename);
663
664 // if the frame was loaded successfully
665 if (loaded != null) {
666 String path = FrameIO.EXPORTS_PATH;
667 String frameName = ImageFrame(loaded, format, path);
668 if (frameName != null)
669 MessageBay.displayMessage("Frame successfully saved to " + path
670 + frameName);
671 else
672 MessageBay.errorMessage("Could not find image writer for "
673 + format + " format");
674 // if the frame was not loaded successfully, alert the user
675 } else {
676 MessageBay.displayMessage("Frame '" + framename
677 + "' could not be found.");
678 }
679 }
680
681 public static void MessageLn(Item message) {
682 if (message instanceof Text)
683 MessageBay.displayMessage((Text) message);
684 }
685
686 /**
687 * Displays a message in the message box area.
688 *
689 * @param message
690 * the message to display
691 */
692 public static void MessageLn(String message) {
693 MessageBay.displayMessage(message);
694 }
695
696 public static void MessageLn2(String message, String message2) {
697 MessageBay.displayMessage(message + " " + message2);
698 }
699
700 public static void FramesetMigrateImages() {
701 final Frame current = DisplayController.getCurrentFrame();
702 final String frameset = current.getFramesetName();
703 final int lastNumber = FrameIO.getLastNumber(frameset);
704 for(int i = 0; i <= lastNumber; i++) {
705 MigrateImages(FrameUtils.getFrame(frameset + i));
706 }
707 }
708
709 public static void MigrateImages() { MigrateImages(DisplayController.getCurrentFrame()); }
710
711 public static void MigrateImages(Frame frame) {
712 //Collect the images on frame
713 final Collection<Item> items = frame.getSortedItems();
714 final Collection<Item> imagesTextItems = new LinkedList<Item>();
715 items.forEach(i -> { if(i.getText().startsWith("@i")) imagesTextItems.add(i); });
716 final Map<Item, Collection<Path>> images = new HashMap<Item, Collection<Path>>();
717 imagesTextItems.forEach(it -> {
718 final Collection<? extends XRayable> enclosures = it.getEnclosures();
719 final Collection<Path> paths = new LinkedList<Path>();
720 enclosures.forEach(enc -> { if(enc instanceof Picture) paths.add(Paths.get(enc.getName())); });
721 images.put(it, paths);
722 });
723
724 //Separate into categories: absolute external, absolute internal. Discard relative.
725 final Map<Item, Collection<Path>> imagesAbsolute = new HashMap<Item, Collection<Path>>();
726 images.keySet().forEach(key -> images.get(key).forEach(path -> {
727 if(path.isAbsolute())
728 if(imagesAbsolute.containsKey(key)) imagesAbsolute.get(key).add(path);
729 else {
730 final Collection<Path> paths = new LinkedList<Path>();
731 paths.add(path);
732 imagesAbsolute.put(key, paths);
733 }
734 }));
735 final Path imagesPath = Paths.get(FrameIO.IMAGES_PATH);
736 final Map<Item, Collection<Path>> imagesAbsoluteInternal = new HashMap<Item, Collection<Path>>();
737 imagesAbsolute.keySet().forEach(key -> imagesAbsolute.get(key).forEach(path -> {
738 if(path.startsWith(imagesPath))
739 if(imagesAbsoluteInternal.containsKey(key)) imagesAbsoluteInternal.get(key).add(path);
740 else {
741 final Collection<Path> paths = new LinkedList<Path>();
742 paths.add(path);
743 imagesAbsoluteInternal.put(key, paths);
744 }
745 }));
746 final Map<Item, Collection<Path>> imagesAbsoluteExternal = new HashMap<Item, Collection<Path>>();
747 imagesAbsolute.keySet().forEach(key -> imagesAbsolute.get(key).forEach(path -> {
748 if(!path.startsWith(imagesPath))
749 if(imagesAbsoluteExternal.containsKey(key)) imagesAbsoluteExternal.get(key).add(path);
750 else {
751 final Collection<Path> paths = new LinkedList<Path>();
752 paths.add(path);
753 imagesAbsoluteExternal.put(key, paths);
754 }
755 }));
756
757 //Bryce: I am not sure why each Item is programmed to be able to have a collection of XRayables rather
758 //than a single one. Up until this point I have programmed defensively to retain this possibility. At
759 //this point the code will begin to simply use the first XRayable to determine the content of the @i.
760
761 //Transform absolute internal images into relative.
762 imagesAbsoluteInternal.keySet().forEach(key -> {
763 final Path imagePath = imagesAbsoluteInternal.get(key).iterator().next();
764 final Path relative = imagesPath.relativize(imagePath);
765 key.setText(key.getText().replace(imagePath.toString(), relative.toString()));
766 MessageBay.displayMessage("Migrated image: " + imagePath + ". It now uses the relative path: " + relative);
767 });
768
769 //Transform absolute external images into relative
770 imagesAbsoluteExternal.keySet().forEach(key -> {
771 final Path imagePath = imagesAbsoluteExternal.get(key).iterator().next();
772 try {
773 Path p = imagesPath.resolve(imagePath.getFileName());
774 FrameIO.copyFile(imagePath.toString(), p.toString());
775 final Path relative = imagesPath.relativize(p);
776 key.setText(key.getText().replace(imagePath.toString(), relative.toString()));
777 MessageBay.displayMessage("Migrated image: " + imagePath + ". It now uses the relative path: " + relative);
778 } catch (IOException e) {
779 MessageBay.displayMessage("Unable to Migrate file: " + imagePath);
780 }
781 });
782 }
783
784 public static void CopyFile(String existingFile, String newFileName) {
785 try {
786 // TODO is there a built in method which will do this faster?
787
788 MessageBay.displayMessage("Copying file " + existingFile + " to "
789 + newFileName + "...");
790 FrameIO.copyFile(existingFile, newFileName);
791 MessageBay.displayMessage("File copied successfully");
792 } catch (FileNotFoundException e) {
793 MessageBay.displayMessage("Error opening file: " + existingFile);
794 } catch (Exception e) {
795 MessageBay.displayMessage("File could not be copied");
796 }
797 }
798
799 /**
800 * Runs two methods alternatively a specified number of times and reports on
801 * the time spent running each method.
802 *
803 * @param fullMethodNameA
804 * @param fullMethodNameB
805 * @param repsPerTest
806 * the number of time each method is run per test
807 * @param tests
808 * the number of tests to conduct
809 *
810 */
811 public static void CompareMethods(String fullMethodNameA,
812 String fullMethodNameB, int repsPerTest, int tests) {
813 try {
814 String classNameA = getClassName(fullMethodNameA);
815 String classNameB = getClassName(fullMethodNameB);
816 String methodNameA = getMethodName(fullMethodNameA);
817 String methodNameB = getMethodName(fullMethodNameB);
818
819 Class<?> classA = Class.forName(classNameA);
820 Class<?> classB = Class.forName(classNameB);
821 Method methodA = classA.getDeclaredMethod(methodNameA,
822 new Class[] {});
823 Method methodB = classB.getDeclaredMethod(methodNameB,
824 new Class[] {});
825 TimeKeeper timeKeeper = new TimeKeeper();
826 long timeA = 0;
827 long timeB = 0;
828 // Run the tests
829 for (int i = 0; i < tests; i++) {
830 // Test methodA
831 timeKeeper.restart();
832 for (int j = 0; j < repsPerTest; j++) {
833 methodA.invoke((Object) null, new Object[] {});
834 }
835 timeA += timeKeeper.getElapsedMillis();
836 timeKeeper.restart();
837 // Test methodB
838 for (int j = 0; j < repsPerTest; j++) {
839 methodB.invoke((Object) null, new Object[] {});
840 }
841 timeB += timeKeeper.getElapsedMillis();
842 }
843
844 float aveTimeA = timeA * 1000F / repsPerTest / tests;
845 float aveTimeB = timeB * 1000F / repsPerTest / tests;
846 // Display Results
847 MessageBay.displayMessage("Average Execution Time");
848 MessageBay.displayMessage(methodNameA + ": "
849 + TimeKeeper.Formatter.format(aveTimeA) + "us");
850 MessageBay.displayMessage(methodNameB + ": "
851 + TimeKeeper.Formatter.format(aveTimeB) + "us");
852 } catch (Exception e) {
853 MessageBay.errorMessage(e.getClass().getSimpleName() + ": "
854 + e.getMessage());
855 }
856 }
857
858 public static String getClassName(String fullMethodName) {
859 assert (fullMethodName != null);
860 assert (fullMethodName.length() > 0);
861 int lastPeriod = fullMethodName.lastIndexOf('.');
862 if (lastPeriod > 0 && lastPeriod < fullMethodName.length() - 1)
863 return fullMethodName.substring(0, lastPeriod);
864 throw new RuntimeException("Invalid method name: " + fullMethodName);
865 }
866
867 public static String getMethodName(String methodName) {
868 assert (methodName != null);
869 assert (methodName.length() > 0);
870 int lastPeriod = methodName.lastIndexOf('.');
871 if (lastPeriod > 0 && lastPeriod < methodName.length() - 1)
872 return methodName.substring(1 + lastPeriod);
873 throw new RuntimeException("Invalid method name: " + methodName);
874 }
875
876 /**
877 * Loads the Frame linked to by the given Item. The first Item on the Frame
878 * that is not the title or name is then placed on the current frame. The
879 * item that was clicked on is placed on the frame it was linked to and the
880 * link is switched to the item from the child frame. If the given Item has
881 * no link, or no item is found then this is a no-op.
882 *
883 * @param current
884 * The Item that links to the Frame that the Item will be loaded
885 * from.
886 */
887 public static void SwapItemWithItemOnChildFrame(Item current) {
888 Item item = getFirstBodyItemOnChildFrame(current, false);
889 // if no item was found
890 if (item == null) {
891 return;
892 }
893
894 // swap the items parents
895 Frame parentFrame = current.getParent();
896 Frame childFrame = item.getParent();
897 current.setParent(childFrame);
898 item.setParent(parentFrame);
899
900 // swap the items on the frames
901 parentFrame.removeItem(current);
902 childFrame.removeItem(item);
903 parentFrame.addItem(item);
904 childFrame.addItem(current);
905
906 // swap the items links
907 item.setActions(current.getAction());
908 item.setLink(childFrame.getName());
909 current.setLink(parentFrame.getName());
910 // current.setLink(null);
911 current.setActions(null);
912
913 DisplayController.requestRefresh(true);
914 }
915
916 private static Item getFirstBodyItemOnChildFrame(Item current,
917 boolean textOnly) {
918 // the item must link to a frame
919 if (current.getLink() == null) {
920 MessageBay
921 .displayMessage("Cannot get item from child - this item has no link");
922 return null;
923 }
924
925 Frame child = FrameIO.LoadFrame(current.getAbsoluteLink());
926
927 // if the frame could not be loaded
928 if (child == null) {
929 MessageBay.errorMessage("Could not load child frame.");
930 return null;
931 }
932
933 // find the first non-title and non-name item
934 List<Item> body = new ArrayList<Item>();
935 if (textOnly)
936 body.addAll(child.getBodyTextItems(false));
937 else
938 body.addAll(child.getSortedItems());
939 Item item = null;
940
941 for (Item i : body)
942 if (i != child.getTitleItem() && !i.isAnnotation()) {
943 item = i;
944 break;
945 }
946
947 // if no item was found
948 if (item == null) {
949 MessageBay.displayMessage("No item found to copy");
950 return null;
951 }
952
953 return item;
954 }
955
956 private static Collection<Item> getItemsOnChildFrame(Item current,
957 boolean textOnly) {
958 // the item must link to a frame
959 if (current.getLink() == null) {
960 MessageBay
961 .displayMessage("Cannot get item from child - this item has no link");
962 return null;
963 }
964 Frame child = FrameIO.LoadFrame(current.getAbsoluteLink());
965
966 // if the frame could not be loaded
967 if (child == null) {
968 MessageBay.errorMessage("Could not load child frame.");
969 return null;
970 }
971
972 // find the first non-title and non-name item
973 Collection<Item> body = new ArrayList<Item>();
974 if (textOnly)
975 body.addAll(child.getBodyTextItems(false));
976 else
977 body.addAll(child.getSortedItems());
978
979 return body;
980 }
981
982 public static void calculate(Frame frame, Item toCalculate) {
983 if (toCalculate instanceof Text) {
984 Text text = (Text) toCalculate;
985 ExpediteeJEP myParser = new ExpediteeJEP();
986 myParser.addVariables(frame);
987 String linkedFrame = toCalculate.getAbsoluteLink();
988 if (linkedFrame != null) {
989 myParser.addVariables(FrameIO.LoadFrame(linkedFrame));
990 }
991 myParser.resetObserver();
992
993 // Do the calculation
994 String formulaFullCase = text.getText().replace('\n', ' ');
995 String formula = formulaFullCase.toLowerCase();
996
997 try {
998 Node node = myParser.parse(formula);
999 Object result = myParser.evaluate(node);
1000 text.setText(result.toString(), true);
1001 text.setFormula(formulaFullCase);
1002 if (text.isFloating()) {
1003 Point cursorPos = EcosystemManager.getInputManager().getCursorPosition();
1004 text.setPosition(cursorPos.getX(), cursorPos.getY());
1005 StandardGestureActions.resetOffset();
1006 } else {
1007 text.getParentOrCurrentFrame().change();
1008 }
1009 } catch (ParseException e) {
1010 MessageBay.errorMessage("Parse error "
1011 + e.getMessage().replace("\n", ""));
1012 } catch (Exception e) {
1013 MessageBay.errorMessage("evaluation error "
1014 + e.getMessage().replace("\n", ""));
1015 e.printStackTrace();
1016 }
1017 }
1018 }
1019
1020 /**
1021 * Attach an item to the cursor.
1022 *
1023 * @param item
1024 */
1025 public static void attachToCursor(Item item) {
1026 item.setParent(null);
1027 StandardGestureActions.pickup(item);
1028 DisplayController.requestRefresh(true);
1029 }
1030
1031 public static void attachToCursor(Collection<Item> items) {
1032 for (Item i : items) {
1033 i.setParent(null);
1034 i.invalidateAll();
1035 }
1036 StandardGestureActions.pickup(items);
1037 // TODO figure out why this isnt repainting stuff immediately
1038 // All of text item doesnt repaint until the cursor is moved
1039 DisplayController.requestRefresh(true);
1040 }
1041
1042 public static void importFiles(Item item) {
1043 List<File> files = new LinkedList<File>();
1044 for (String s : item.getText().split("\\s+")) {
1045 File file = new File(s.trim());
1046 if (file.exists()) {
1047 files.add(file);
1048 }
1049 }
1050 try {
1051 EcosystemManager.getDragAndDropManager().importFileList(files, EcosystemManager.getInputManager().getCursorPosition(), false);
1052 } catch (Exception e) {
1053 }
1054 }
1055
1056 public static void importFile(Item item) {
1057 File file = new File(item.getText().trim());
1058 if (file.exists()) {
1059 try {
1060 EcosystemManager.getDragAndDropManager().importFile(file, EcosystemManager.getInputManager().getCursorPosition(), false);
1061 } catch (Exception e) {
1062 e.printStackTrace();
1063 }
1064 }
1065 }
1066
1067 public static Item createPolygon(Item item, int sides) {
1068 if (item instanceof Text) {
1069 try {
1070 SString s = new SString(item.getText());
1071 sides = s.integerValue().intValue();
1072 } catch (NumberFormatException e) {
1073 }
1074 }
1075
1076 if (sides < 3) {
1077 MessageBay.errorMessage("Shapes must have at least 3 sides");
1078 }
1079 double angle = -(180 - ((sides - 2) * 180.0F) / sides);
1080 double curAngle = 0;
1081 double size = 50F;
1082 if (item.isLineEnd() && item.getLines().size() > 0) {
1083 item = item.getLines().get(0);
1084 }
1085 // Use line length to determine the size of the shape
1086 if (item instanceof Line) {
1087 size = ((Line) item).getLength();
1088 }
1089
1090 Point cursorPos = DisplayController.getMousePosition();
1091 float curX = cursorPos.getX();
1092 float curY = cursorPos.getY();
1093
1094 Collection<Item> newItems = new LinkedList<Item>();
1095 Item[] d = new Item[sides];
1096 // create dots
1097 Frame current = DisplayController.getCurrentFrame();
1098 for (int i = 0; i < d.length; i++) {
1099 d[i] = current.createDot();
1100 newItems.add(d[i]);
1101 d[i].setPosition(curX, curY);
1102 curX += (float) (Math.cos((curAngle) * Math.PI / 180.0) * size);
1103 curY += (float) (Math.sin((curAngle) * Math.PI / 180.0) * size);
1104
1105 curAngle += angle;
1106 }
1107 // create lines
1108 for (int i = 1; i < d.length; i++) {
1109 newItems.add(new Line(d[i - 1], d[i], current.getNextItemID()));
1110 }
1111 newItems.add(new Line(d[d.length - 1], d[0], current.getNextItemID()));
1112
1113 current.addAllItems(newItems);
1114 if (item instanceof Text) {
1115 for (Item i : item.getAllConnected()) {
1116 if (i instanceof Line) {
1117 item = i;
1118 break;
1119 }
1120 }
1121 }
1122
1123 Colour newColor = item.getColor();
1124 if (newColor != null) {
1125 d[0].setColor(item.getColor());
1126 if (item instanceof Text && item.getBackgroundColor() != null) {
1127 d[0].setFillColor(item.getBackgroundColor());
1128 } else {
1129 d[0].setFillColor(item.getFillColor());
1130 }
1131 }
1132 float newThickness = item.getThickness();
1133 if (newThickness > 0) {
1134 d[0].setThickness(newThickness);
1135 }
1136
1137 ItemUtils.EnclosedCheck(newItems);
1138 DisplayController.requestRefresh(false);
1139
1140 return d[0];
1141 }
1142
1143 public static void StopReminder() {
1144 Reminders.stop();
1145 }
1146
1147 public static void print(String file)
1148 {
1149 String errorMessage = EcosystemManager.getMiscManager().print(file);
1150
1151 if (errorMessage != null) MessageBay.errorMessage("Printing error: " + errorMessage);
1152 }
1153
1154 public static int wordCount(String paragraph) {
1155 return paragraph.trim().split("\\s+").length + 1;
1156 }
1157
1158 public static int wordCount(Frame frame) {
1159 int count = 0;
1160
1161 for (Text t : frame.getBodyTextItems(false)) {
1162 count += wordCount(t.getText());
1163 }
1164
1165 return count;
1166 }
1167
1168 public static void moveToPublic(Frame frame) {
1169 FrameIO.moveFrameset(frame.getFramesetName(), FrameIO.PUBLIC_PATH, false);
1170 }
1171
1172 public static void moveToPrivate(Frame frame) {
1173 FrameIO.moveFrameset(frame.getFramesetName(), FrameIO.FRAME_PATH, false);
1174 }
1175
1176 /**
1177 * Returns the value of a specified item attribute.
1178 *
1179 * @param item
1180 * from which to extract the value
1181 * @param attribute
1182 * name of an items attribute
1183 * @return the value of the attribute
1184 */
1185 public static String extract(Item item, String attribute) {
1186 return AttributeUtils.getAttribute(item, attribute);
1187 }
1188
1189 /**
1190 * Launches items.widgets.Browser and uses Text item as URL.
1191 * @param text Text item which passes contents as URL for browser.
1192 * @throws Exception
1193 */
1194/* public static void startLoboBrowser(Item text) throws Exception {
1195 if (!(text instanceof Text)) {
1196 MessageBay.errorMessage("Must be a text item.");
1197 return;
1198 }
1199 if(text.getLink() != null) {
1200 MessageBay.errorMessage("Text item cannot have link.");
1201 return;
1202 }
1203
1204 FreeItems.getInstance().clear(); // remove url text from cursor
1205
1206 Text wt = new Text("@iw:org.expeditee.items.widgets.Browser"); // create new text item for browser widget
1207 wt.setParent(DisplayIO.getCurrentFrame()); // set parent of text source for InteractiveWidget.createWidget()
1208 wt.setXY(FrameMouseActions.getX(), FrameMouseActions.getY());
1209 // create widget from text item
1210 org.expeditee.items.widgets.Browser browser = (org.expeditee.items.widgets.Browser) InteractiveWidget.createWidget(wt);
1211
1212 if(FreeItems.textOnlyAttachedToCursor()) { // navigates to url specified by the text item
1213 browser.navigate(text.getText());
1214 } else {
1215 browser.navigate("http://www.waikato.ac.nz");
1216 }
1217
1218 FrameMouseActions.pickup(browser.getItems()); // attach browser widget to mouse
1219 }*/
1220
1221 /**
1222 * Text item becomes link to new frame containing items.widgets.Browser and uses Text item as URL for browser.
1223 * @param text Text item which passes contents as URL for browser and becomes link to the browser's new frame.
1224 * @throws Exception
1225 */
1226 public static void startLoboBrowserNewFrame(Item text) throws Exception
1227 {
1228 if (!(text instanceof Text)) {
1229 MessageBay.errorMessage("Must be a text item.");
1230 return;
1231 }
1232
1233 if(text.getLink() != null) { // text item can't already have a link
1234 MessageBay.errorMessage("Text item already has link.");
1235 return;
1236 }
1237
1238 // Create new frame and text item for browser widget and parse created frame; loads browser widget
1239 Frame frame = FrameIO.CreateNewFrame(text);
1240 frame.addText(0, 50, "@iw:org.expeditee.items.widgets.Browser", null);
1241 FrameUtils.Parse(frame);
1242
1243 for(Widget iw : frame.getInteractiveWidgets()) { // may be other widgets on frame
1244 if(iw instanceof org.expeditee.items.widgets.Browser) {
1245 // Set browser to 'full screen'
1246 iw.setSize(-1, -1, -1, -1, DisplayController.getFramePaintAreaWidth(), DisplayController.getFramePaintAreaHeight() - 80);
1247
1248 // If there is a text item attached to cursor use it as url for browser
1249 if (FreeItems.textOnlyAttachedToCursor()) {
1250 text.setLink("" + frame.getNumber());
1251 ((org.expeditee.items.widgets.Browser)iw).navigate(text.getText());
1252 } else {
1253 // Navigate to www.waikato.ac.nz by default if no url supplied and create new text item to be the link
1254 ((org.expeditee.items.widgets.Browser)iw).navigate("http://www.waikato.ac.nz");
1255 Text t = new Text("http://www.waikato.ac.nz");
1256 t.setParent(DisplayController.getCurrentFrame()); // set parent of text source for InteractiveWidget.createWidget()
1257 t.setXY(DisplayController.getFloatMouseX(), DisplayController.getFloatMouseY());
1258 t.setLink("" + frame.getNumber()); // link url text to new browser frame
1259 StandardGestureActions.pickup(t); // Attach new text link to cursor
1260 }
1261 }
1262 }
1263
1264 FrameIO.SaveFrame(frame); // save frame to disk
1265 }
1266
1267 private static boolean startWidget(String name) throws Exception
1268 {
1269 String fullName = Actions.getClassName(name);
1270 if(fullName == null) {
1271 return false;
1272 }
1273 MessageBay.displayMessage("Creating new \"" + fullName + "\"");
1274
1275 FreeItems.getInstance().clear();
1276 Text wt = new Text("@iw:" + fullName); // create new text item for browser widget
1277 wt.setParent(DisplayController.getCurrentFrame()); // set parent of text source for InteractiveWidget.createWidget()
1278 wt.setXY(DisplayController.getMouseX(), DisplayController.getMouseY()); // move to the mouse cursor
1279 Widget widget = Widget.createWidget(wt);
1280 StandardGestureActions.pickup(widget.getItems());
1281
1282 return true;
1283 }
1284
1285 private static void runUnknown(String command) throws Exception {
1286 if(startWidget(command)) {
1287 return;
1288 }
1289
1290 Actions.PerformAction(DisplayController.getCurrentFrame(), null, command, null);
1291 }
1292
1293 public static void run(String command) throws Exception {
1294 if(command == null) {
1295 MessageBay.warningMessage("Please provide a command to run");
1296 return;
1297 }
1298 int firstSpace = command.indexOf(" ");
1299 if(firstSpace == -1) {
1300 runUnknown(command);
1301 return;
1302 }
1303 String argLower = command.toLowerCase();
1304 String name = argLower.substring(0, firstSpace).trim(); // first word
1305 String args = argLower.substring(firstSpace).trim(); // remainder after first word
1306 if(name == "action" || name == "agent") {
1307 if(args.length() > 0) {
1308 Actions.LegacyPerformAction(DisplayController.getCurrentFrame(), null, args);
1309 } else {
1310 MessageBay.displayMessage("Please specify an action/agent name");
1311 }
1312 } else if(name == "widget") {
1313 if(args.length() > 0) {
1314 if(!startWidget(args)) {
1315 MessageBay.displayMessage("Widget \"" + name + "\" does not exist");
1316 }
1317 } else {
1318 MessageBay.displayMessage("Please specify a widget name");
1319 }
1320 } else {
1321 runUnknown(command);
1322 }
1323 }
1324
1325 public static void run(Item item) throws Exception {
1326 if(item == null) {
1327 MessageBay.warningMessage("Please provide a command to run");
1328 return;
1329 }
1330 run(((Text)item).getText());
1331 }
1332
1333 /**
1334 * Rebuilds the home frame restoring its original presentation.
1335 * Basically removes all items on the frame and reruns FrameUtils.CreateDefaultProfile().
1336 */
1337 public static void resetHomeFrame() {
1338 Frame homeFrame = FrameIO.LoadFrame(UserSettings.HomeFrame.get());
1339 homeFrame.removeAllItems(homeFrame.getSortedItems());
1340 homeFrame.addText(0, 0, "title", null);
1341 FrameUtils.CreateDefaultProfile(UserSettings.UserName.get(), homeFrame, null, null);
1342 }
1343
1344 /**
1345 * Loads and runs an executable jar file in a new Thread
1346 * @param jar path to the jar file to run
1347 */
1348 public static void runJar(String jar) throws Exception {
1349 File jf = new File(jar);
1350 if(!jf.exists()) {
1351 System.err.println("jar '" + jar + "' could not be found");
1352 return;
1353 }
1354 JarFile jarFile = new JarFile(jf);
1355
1356 String mainClassName = (String) jarFile.getManifest().getMainAttributes().get(new Attributes.Name("Main-Class"));
1357 if(mainClassName == null) {
1358 System.err.println("jar '" + jar + "' does not have a Main-Class entry");
1359 jarFile.close();
1360 return;
1361 }
1362 jarFile.close();
1363 System.out.println("Main-Class = " + mainClassName);
1364
1365 ClassLoader classLoader = ClassLoader.getSystemClassLoader();
1366
1367 Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
1368 addURL.setAccessible(true);
1369 addURL.invoke(classLoader, jf.toURI().toURL());
1370
1371 final Class<?> jarClass = classLoader.loadClass(mainClassName);
1372
1373 final Method main = jarClass.getDeclaredMethod("main", String[].class);
1374
1375 new Thread(new Runnable() {
1376 public void run() {
1377 try {
1378 main.invoke(jarClass, new Object[] {new String[0]});
1379 } catch (Exception e) {
1380 System.out.println("Failed to start jar");
1381 e.printStackTrace();
1382 }
1383 }
1384 }).start();
1385 }
1386
1387 public static void pan(Frame frame, int x, int y) {
1388 for (Item i : frame.getAllItems()) {
1389 if (i instanceof WidgetEdge || i instanceof WidgetCorner) {
1390 continue;
1391 } else {
1392 int new_x = i.getX();
1393 int new_y = i.getY();
1394
1395 if (!i.isAnchoredX()) {
1396 new_x += x;
1397 }
1398
1399 if (!i.isAnchoredY()) {
1400 new_y += y;
1401 }
1402
1403 if(i instanceof XRayable) {
1404 i.setPosition(new_x,new_y);
1405 }
1406 else {
1407 i.setXY(new_x,new_y);
1408 }
1409 }
1410 // update the polygon, otherwise stuff moves but leaves it's outline behind
1411 i.invalidateBounds();
1412 }
1413
1414 for (Widget iw : frame.getInteractiveWidgets()) {
1415
1416 int new_x = iw.getX();
1417 int new_y = iw.getY();
1418
1419 if (!iw.isAnchoredX()) {
1420 new_x += x;
1421 }
1422
1423 if (!iw.isAnchoredY()) {
1424 new_y += y;
1425 }
1426
1427 iw.setPosition(new_x,new_y);
1428
1429 }
1430
1431 // make sure we save the panning of the frame
1432 frame.change();
1433 // redraw everything
1434 StandardGestureActions.Refresh();
1435 }
1436
1437 public static void pan(Frame frame, String pan) {
1438 String[] split = pan.split("\\s+");
1439 int x = 0;
1440 int y = 0;
1441 try {
1442 if(split.length != 2) throw new Exception();
1443 x = Integer.parseInt(split[0]);
1444 y = Integer.parseInt(split[1]);
1445 } catch(Exception e) {
1446 MessageBay.errorMessage("Panning takes 2 integer arguments");
1447 return;
1448 }
1449 pan(frame, x, y);
1450 }
1451
1452 public static void pan(Frame frame, Text pan) {
1453 pan(frame, pan.getText());
1454 }
1455
1456 public static String exec(String cmd) throws Exception {
1457
1458 String[] command;
1459
1460 // run command through sh if possible
1461 if(System.getProperty("os.name").toLowerCase().indexOf("win") == -1) {
1462 command = new String[] { "sh", "-c", cmd };
1463 } else {
1464 command = cmd.split("\\s+");
1465 }
1466
1467 ProcessBuilder pb = new ProcessBuilder(command);
1468 pb.redirectErrorStream(true);
1469 Process ps = pb.start();
1470
1471 BufferedReader in = new BufferedReader(new InputStreamReader(ps.getInputStream()));
1472 StringBuffer sb = new StringBuffer();
1473 String line;
1474 while ((line = in.readLine()) != null) {
1475 sb.append(line).append('\n');
1476 }
1477 ps.waitFor();
1478 in.close();
1479
1480 if(sb.length() > 0) {
1481 sb.deleteCharAt(sb.length() - 1);
1482 }
1483 return sb.toString();
1484 }
1485
1486 public static void testProgress() {
1487 new Thread(new Runnable() {
1488
1489 @Override
1490 public void run() {
1491 Progress p = MessageBay.displayProgress("Loading something");
1492 for(int i = 1; i <= 100; i++) {
1493 try {
1494 Thread.sleep(100);
1495 p.set(i);
1496 } catch (Exception e) {
1497 e.printStackTrace();
1498 }
1499 }
1500 }
1501
1502 }).start();
1503 }
1504
1505 public static void getIDs(Frame f) {
1506 for(Item i : f.getAllItems()) {
1507 System.out.println(i + " (" + i.getID() + ")");
1508 }
1509 }
1510
1511 public static void flushResources() {
1512 FrameUtils.extractResources(true);
1513 MessageBay.displayMessage("Re-extracted resources, Expeditee may need to be restarted for certain resources to be reloaded");
1514 }
1515
1516 // Some experimental actions to do with keeping framesets stored within a Git repository
1517
1518
1519 // For custom merge, some potentially useful information at:
1520 // http://stackoverflow.com/questions/23140240/git-how-do-i-add-a-custom-merge-strategy
1521 // http://stackoverflow.com/questions/7607125/git-merge-conflict-to-always-take-the-newest-file
1522
1523 protected static String gitexe = "git";
1524
1525 protected static void runGitCommand(Frame frame, List<String> cmd_array) {
1526
1527 String framePath = frame.getPath();
1528 String frameName = frame.getName();
1529
1530 String frameDir = frame.getFramesetPath(); //framePath + Conversion.getFramesetName(frameName) + File.separator;
1531 String localFname = Conversion.getFrameNumber(frameName)
1532 + ExpReader.EXTENTION;
1533
1534 ProcessBuilder process_builder = new ProcessBuilder(cmd_array);
1535
1536 process_builder.directory(new File(frameDir));
1537
1538 /*
1539 System.err.println("\nPATH:");
1540
1541 Map<String, String> env_map = process_builder.environment();
1542
1543 for (Entry<String, String> entry: env_map.entrySet()) {
1544 String key = entry.getKey();
1545 String value = entry.getValue();
1546 System.err.println(key + " = " + value);
1547 }
1548 */
1549
1550
1551 try {
1552 final Process process = process_builder.start();
1553 InputStream is = process.getInputStream();
1554 InputStreamReader isr = new InputStreamReader(is);
1555 BufferedReader br = new BufferedReader(isr);
1556 String line;
1557 while ((line = br.readLine()) != null) {
1558 System.out.println(line);
1559 }
1560 System.out.println("Program terminated!");
1561 }
1562 catch (Exception e) {
1563 e.printStackTrace();
1564 }
1565
1566 }
1567
1568 public static void GitPushFrame() {
1569
1570 StandardGestureActions.Save();
1571
1572 Frame current = DisplayController.getCurrentFrame();
1573 String userName = UserSettings.UserName.get();
1574
1575 String frameName = current.getName();
1576 String localFname = Conversion.getFrameNumber(frameName)+ ExpReader.EXTENTION;
1577
1578 List<String> status_cmd_array = new ArrayList<String>();
1579 status_cmd_array.add(gitexe);
1580 status_cmd_array.add("status");
1581 status_cmd_array.add(".");
1582
1583 List<String> add_cmd_array = new ArrayList<String>();
1584 add_cmd_array.add(gitexe);
1585 add_cmd_array.add("add");
1586 add_cmd_array.add(localFname);
1587 add_cmd_array.add("frame.inf");
1588
1589 runGitCommand(current,add_cmd_array);
1590
1591 List<String> commit_cmd_array = new ArrayList<String>();
1592 commit_cmd_array.add(gitexe);
1593 commit_cmd_array.add("commit");
1594 commit_cmd_array.add("-m");
1595 commit_cmd_array.add("expeditee-edit-"+userName);
1596
1597 runGitCommand(current,commit_cmd_array);
1598
1599 List<String> push_cmd_array = new ArrayList<String>();
1600 push_cmd_array.add(gitexe);
1601 push_cmd_array.add("push");
1602 push_cmd_array.add("origin");
1603 push_cmd_array.add("master");
1604
1605 runGitCommand(current,push_cmd_array);
1606 }
1607
1608 public static void GitPullFrame() {
1609 Frame current = DisplayController.getCurrentFrame();
1610
1611 List<String> cmd_array = new ArrayList<String>();
1612 cmd_array.add(gitexe);
1613 cmd_array.add("pull");
1614 cmd_array.add("origin");
1615 cmd_array.add("master");
1616 runGitCommand(current,cmd_array);
1617
1618 FrameIO.Reload();
1619 StandardGestureActions.Refresh();
1620 }
1621
1622}
Note: See TracBrowser for help on using the repository browser.