2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2010 - DIGITEO - Clement DAVID
4 * Copyright (C) 2011-2015 - Scilab Enterprises - Clement DAVID
6 * Copyright (C) 2012 - 2016 - Scilab Enterprises
8 * This file is hereby licensed under the terms of the GNU GPL v2.0,
9 * pursuant to article 5.3.4 of the CeCILL v.2.1.
10 * This file was originally licensed under the terms of the CeCILL v2.1,
11 * and continues to be available under such terms.
12 * For more information, see the COPYING file which you should have received
13 * along with this program.
17 package org.scilab.modules.xcos.palette;
19 import java.awt.GraphicsEnvironment;
20 import java.awt.image.BufferedImage;
22 import java.io.IOException;
23 import java.lang.reflect.InvocationTargetException;
24 import java.util.ArrayList;
25 import java.util.Deque;
26 import java.util.LinkedList;
27 import java.util.List;
28 import java.util.logging.Level;
29 import java.util.logging.Logger;
31 import javax.imageio.ImageIO;
32 import javax.swing.SwingUtilities;
34 import org.scilab.modules.graph.utils.ScilabExported;
35 import org.scilab.modules.javasci.JavasciException;
36 import org.scilab.modules.javasci.Scilab;
37 import org.scilab.modules.localization.Messages;
38 import org.scilab.modules.types.ScilabDouble;
39 import org.scilab.modules.types.ScilabTList;
40 import org.scilab.modules.types.ScilabType;
41 import org.scilab.modules.xcos.JavaController;
42 import org.scilab.modules.xcos.Kind;
43 import org.scilab.modules.xcos.Xcos;
44 import org.scilab.modules.xcos.block.BasicBlock;
45 import org.scilab.modules.xcos.graph.XcosDiagram;
46 import org.scilab.modules.xcos.io.scicos.ScicosFormatException;
47 import org.scilab.modules.xcos.palette.model.Category;
48 import org.scilab.modules.xcos.palette.model.PaletteBlock;
49 import org.scilab.modules.xcos.palette.model.PaletteNode;
50 import org.scilab.modules.xcos.palette.model.PreLoaded;
51 import org.scilab.modules.xcos.utils.BlockPositioning;
52 import org.scilab.modules.xcos.utils.XcosConstants;
54 import com.mxgraph.swing.mxGraphComponent;
55 import com.mxgraph.util.mxCellRenderer;
56 import com.mxgraph.util.mxRectangle;
57 import com.mxgraph.view.mxGraphView;
58 import com.mxgraph.view.mxStylesheet;
59 import org.scilab.modules.xcos.ObjectProperties;
62 * Utility class which is the entry point from Scilab for palette related
65 public final class Palette {
66 /** the "name" argument */
67 public static final String NAME = "name";
69 /** Error message used on invalid path */
70 public static final String WRONG_INPUT_ARGUMENT_S_INVALID_TREE_PATH = Messages.gettext("Wrong input argument \"%s\": invalid tree path.\n");
71 /** Error message used on invalid node */
72 public static final String WRONG_INPUT_ARGUMENT_S_INVALID_NODE = Messages
73 .gettext("Wrong input argument \"%s\": invalid node, use 'xcosPalDisable' instead.\n");
74 /** "Unable to import" string */
75 public static final String UNABLE_TO_IMPORT = Messages.gettext("Unable to import %s .\n");
77 private static final Logger LOG = Logger.getLogger(Palette.class.getName());
78 private static final String XCOS = "xcos";
79 private static final String PALETTE_GIWS_XML = "Palette.giws.xml";
82 * Default hidden constructor
88 * Get the {@link PaletteNode} of the path.
93 * if <code>true</code> the Category path will be created,
94 * otherwise it will not.
95 * @return the selected node
97 private static PaletteNode getPathNode(final String[] path, final boolean create) {
99 if (!SwingUtilities.isEventDispatchThread()) {
100 throw new RuntimeException("Unable to manipulate palette outside the EDT thread.");
103 Category node = PaletteManager.getInstance().getRoot();
105 if (path == null || path.length == 0 || (path.length == 1 && path[0].isEmpty())) {
109 for (int categoryCounter = 0; categoryCounter < path.length; categoryCounter++) {
111 for (final PaletteNode next : node.getNode()) {
112 if (next.getName().equals(path[categoryCounter]) && next instanceof Category) {
113 node = (Category) next;
115 } else if (next.getName().equals(path[categoryCounter]) && (categoryCounter == path.length - 1)) {
116 return next; // found the terminal Palette instance
120 if (!node.toString().equals(path[categoryCounter])) {
122 final Category cat = new Category();
123 cat.setName(path[categoryCounter]);
124 cat.setEnable(create);
127 node.getNode().add(cat);
139 * Load an xcos palette into the palette manager
142 * the scilab exported palette variable name
144 * TreePath of the palette
145 * @throws JavasciException
146 * on invocation error
148 @ScilabExported(module = XCOS, filename = PALETTE_GIWS_XML)
149 public static void loadPal(final String name, final String[] category) throws JavasciException {
151 * If the env. is headless only perform fake loading to assert data
154 if (GraphicsEnvironment.isHeadless()) {
155 LOG.warning("Headless environment detected, only perform sanity check");
156 loadPalHeadless(name);
163 final ScilabTList data = (ScilabTList) Scilab.getInCurrentScilabSession(name);
166 * handle shared data on the EDT thread
169 SwingUtilities.invokeAndWait(new Runnable() {
174 * Decode the style part of the palette
176 final mxStylesheet styleSheet = Xcos.getInstance().getStyleSheet();
178 new StyleElement().decode(data, styleSheet);
179 } catch (final ScicosFormatException e) {
180 throw new RuntimeException(e);
183 // reload all the opened diagram (clear states)
184 for (final XcosDiagram d : Xcos.getInstance().openedDiagrams()) {
186 final mxGraphView view = d.getView();
191 final mxGraphComponent comp = d.getAsComponent();
198 final PaletteNode node = getPathNode(category, true);
199 if (!(node instanceof Category)) {
200 throw new RuntimeException(String.format(WRONG_INPUT_ARGUMENT_S_INVALID_TREE_PATH, "category"));
202 final Category cat = (Category) node;
205 * Adding the palette tree part of the palette
209 pal = new PreLoadedElement().decode(data, new PreLoaded.Dynamic());
210 } catch (final ScicosFormatException e) {
211 throw new RuntimeException(e);
213 cat.getNode().add(pal);
216 PaletteNode.refreshView(cat, pal);
217 } catch (Exception e) {
222 } catch (final InterruptedException e) {
223 LOG.severe(e.toString());
224 } catch (final InvocationTargetException e) {
225 Throwable throwable = e;
226 String firstMessage = null;
227 while (throwable != null) {
228 firstMessage = throwable.getLocalizedMessage();
229 throwable = throwable.getCause();
232 throw new RuntimeException(firstMessage, e);
236 private static final void loadPalHeadless(final String name) throws JavasciException {
238 final ScilabTList data = (ScilabTList) Scilab.getInCurrentScilabSession(name);
241 new StyleElement().decode(data, new mxStylesheet());
243 // palette data check
244 new PreLoadedElement().decode(data, new PreLoaded.Dynamic());
246 } catch (ScicosFormatException e) {
247 throw new RuntimeException(e);
252 * Load an xcos palette into the palette manager at the root category.
255 * the scilab exported palette variable name
256 * @throws JavasciException
257 * on invocation error
259 @ScilabExported(module = XCOS, filename = PALETTE_GIWS_XML)
260 public static void loadPal(final String name) throws JavasciException {
265 * Push a block list into Scilab.
267 * The block list is pushed into a "pal" variable, a pseudo palette.
269 * @param path the path used to export the palette tree
270 * @throws JavasciException on pushing error
271 * @throws InterruptedException on wait error
272 * @throws InvocationTargetException on palette creation
274 @ScilabExported(module = XCOS, filename = PALETTE_GIWS_XML)
275 public static void get(final String[] path) throws JavasciException, InvocationTargetException, InterruptedException {
276 SwingUtilities.invokeAndWait(new Runnable() {
279 PaletteNode root = getPathNode(path, false);
282 * Create a pseudo palette
285 if (root instanceof PreLoaded) {
286 pal = (PreLoaded) root;
287 } else if (root instanceof Category) {
288 LinkedList<Category> stash = new LinkedList<Category>();
289 stash.add((Category) root);
291 pal = new PreLoaded();
292 pal.setName(root.getName());
293 pal.getBlock().addAll(list(stash, pal));
300 * Encode the pseudo-palette into a ScilabType
302 final ScilabType element;
304 final PreLoadedElement encoder = new PreLoadedElement();
305 element = encoder.encode(pal, null);
307 element = new ScilabDouble();
311 Scilab.putInCurrentScilabSession("pal", element);
312 } catch (JavasciException e) {
313 throw new RuntimeException(e);
320 private static List<PaletteBlock> list(Deque<Category> stash, PreLoaded pal) {
321 final ArrayList<PaletteBlock> blocks = new ArrayList<PaletteBlock>();
322 while (!stash.isEmpty()) {
323 final Category c = stash.pop();
324 for (PaletteNode n : c.getNode()) {
325 if (n instanceof Category) {
326 stash.add((Category) n);
327 } else if (n instanceof PreLoaded) {
328 final PreLoaded p = (PreLoaded) n;
329 blocks.addAll(p.getBlock());
339 * Add a category into the palette manager
342 * TreePath of the palette
344 * default visibility of the palette
346 @ScilabExported(module = XCOS, filename = PALETTE_GIWS_XML)
347 public static void addCategory(final String[] name, final boolean visible) {
349 SwingUtilities.invokeAndWait(new Runnable() {
352 final PaletteNode node = getPathNode(name, true);
353 if (node instanceof Category) {
354 node.setEnable(visible);
356 throw new RuntimeException(String.format(WRONG_INPUT_ARGUMENT_S_INVALID_TREE_PATH, NAME));
359 PaletteNode.refreshView(node.getParent(), node);
362 } catch (final InterruptedException e) {
363 Logger.getLogger(Palette.class.getName()).severe(e.toString());
364 } catch (final InvocationTargetException e) {
365 Throwable throwable = e;
366 String firstMessage = null;
367 while (throwable != null) {
368 firstMessage = throwable.getLocalizedMessage();
369 throwable = throwable.getCause();
372 throw new RuntimeException(firstMessage, e);
377 * Remove a palette or a category of the palette manager
380 * TreePath of the palette
382 @ScilabExported(module = XCOS, filename = PALETTE_GIWS_XML)
383 public static void remove(final String[] name) {
385 SwingUtilities.invokeAndWait(new Runnable() {
388 final PaletteNode node = getPathNode(name, false);
389 PaletteNode.remove(node);
392 } catch (final InterruptedException e) {
393 LOG.severe(e.toString());
394 } catch (final InvocationTargetException e) {
395 Throwable throwable = e;
396 String firstMessage = null;
397 while (throwable != null) {
398 firstMessage = throwable.getLocalizedMessage();
399 throwable = throwable.getCause();
402 throw new RuntimeException(firstMessage, e);
407 * Remove a palette or a category of the palette manager
410 * TreePath of the palette or category
412 * True to set the palette visible, false otherwise
414 @ScilabExported(module = XCOS, filename = PALETTE_GIWS_XML)
415 public static void enable(final String[] name, final boolean status) {
417 SwingUtilities.invokeAndWait(new Runnable() {
420 final PaletteNode node = getPathNode(name, false);
422 throw new RuntimeException(String.format(WRONG_INPUT_ARGUMENT_S_INVALID_TREE_PATH, NAME));
425 node.setEnable(status);
427 PaletteNode.refreshView(node.getParent(), node);
430 } catch (final InterruptedException e) {
431 LOG.severe(e.toString());
432 } catch (final InvocationTargetException e) {
433 Throwable throwable = e;
434 String firstMessage = null;
435 while (throwable != null) {
436 firstMessage = throwable.getLocalizedMessage();
437 throwable = throwable.getCause();
440 throw new RuntimeException(firstMessage, e);
445 * Move a palette or a category of the palette manager
448 * TreePath of the palette or category
450 * TreePath of the destination
452 @ScilabExported(module = XCOS, filename = PALETTE_GIWS_XML)
453 public static void move(final String[] source, final String[] target) {
455 SwingUtilities.invokeAndWait(new Runnable() {
459 final PaletteNode src = getPathNode(source, false);
461 throw new RuntimeException(String.format(WRONG_INPUT_ARGUMENT_S_INVALID_TREE_PATH, "source"));
464 final PaletteNode trg = getPathNode(target, true);
465 if (trg == null || !(trg instanceof Category)) {
466 throw new RuntimeException(String.format(WRONG_INPUT_ARGUMENT_S_INVALID_TREE_PATH, "target"));
468 final Category destination = (Category) trg;
470 final Category[] toBeReloaded = new Category[] { src.getParent(), destination };
472 if (toBeReloaded[0] != null) {
473 toBeReloaded[0].getNode().remove(src);
475 destination.getNode().add(src);
476 src.setParent(destination);
478 PaletteNode.refreshView(toBeReloaded[0], null);
479 PaletteNode.refreshView(toBeReloaded[1], src);
482 } catch (final InterruptedException e) {
483 LOG.severe(e.toString());
484 } catch (final InvocationTargetException e) {
485 Throwable throwable = e;
486 String firstMessage = null;
487 while (throwable != null) {
488 firstMessage = throwable.getLocalizedMessage();
489 throwable = throwable.getCause();
492 throw new RuntimeException(firstMessage, e);
497 * Generate a palette block image from a block instance stored into scilab
498 * (need a valid style).
501 * the output file path use to save the palette block.
505 @ScilabExported(module = XCOS, filename = PALETTE_GIWS_XML)
506 public static void generatePaletteIcon(final long uid, final String iconPath) throws Exception {
508 * If the env. is headless does nothing
510 if (GraphicsEnvironment.isHeadless()) {
511 LOG.warning("Headless environment detected, do not generate icons");
515 JavaController controller = new JavaController();
516 Kind kind = controller.getKind(uid);
518 String[] strUID = new String[] { "" };
519 controller.getObjectProperty(uid, kind, ObjectProperties.UID, strUID);
521 String[] label = new String[] { "" };
522 controller.getObjectProperty(uid, kind, ObjectProperties.LABEL, label);
524 String[] style = new String[] { "" };
525 controller.getObjectProperty(uid, kind, ObjectProperties.STYLE, style);
527 final BasicBlock block = new BasicBlock(new JavaController(), uid, kind, label[0], null, style[0], strUID[0]);
528 generateIcon(block, iconPath);
530 if (LOG.isLoggable(Level.FINEST)) {
531 LOG.finest(iconPath + " updated.");
535 private static void generateIcon(BasicBlock block, final String iconPath) throws IOException {
536 if (block == null || block.getGeometry() == null) {
539 block.getGeometry().setX(XcosConstants.PALETTE_BLOCK_WIDTH);
540 block.getGeometry().setY(XcosConstants.PALETTE_BLOCK_HEIGHT);
542 JavaController controller = new JavaController();
544 final XcosDiagram graph = new XcosDiagram(controller, controller.createObject(Kind.DIAGRAM), Kind.DIAGRAM, "");
545 graph.installListeners();
547 graph.addCell(block);
550 BlockPositioning.updateBlockView(graph, block);
555 final mxGraphComponent graphComponent = graph.getAsComponent();
556 graphComponent.refresh();
558 final mxRectangle bounds = graph.getPaintBounds(new Object[] { block });
559 final double width = bounds.getWidth();
560 final double height = bounds.getHeight();
563 if (width > XcosConstants.PALETTE_BLOCK_WIDTH || height > XcosConstants.PALETTE_BLOCK_HEIGHT) {
564 scale = Math.min(XcosConstants.PALETTE_BLOCK_WIDTH / width, XcosConstants.PALETTE_BLOCK_HEIGHT / height) / XcosConstants.PALETTE_BLOCK_ICON_RATIO;
569 final BufferedImage image = mxCellRenderer.createBufferedImage(graph, null, scale, graphComponent.getBackground(), graphComponent.isAntiAlias(), null,
570 graphComponent.getCanvas());
572 final String extension = iconPath.substring(iconPath.lastIndexOf('.') + 1);
573 ImageIO.write(image, extension, new File(iconPath));
576 controller.deleteObject(graph.getUID());