2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2010 - DIGITEO - Clement DAVID
5 * This file must be used under the terms of the CeCILL.
6 * This source file is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at
9 * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
13 package org.scilab.modules.xcos.palette;
15 import java.awt.GraphicsEnvironment;
16 import java.awt.image.BufferedImage;
18 import java.io.IOException;
19 import java.lang.reflect.InvocationTargetException;
20 import java.util.List;
21 import java.util.logging.Level;
22 import java.util.logging.Logger;
24 import javax.imageio.ImageIO;
25 import javax.swing.SwingUtilities;
27 import org.scilab.modules.graph.utils.ScilabExported;
28 import org.scilab.modules.javasci.JavasciException;
29 import org.scilab.modules.javasci.Scilab;
30 import org.scilab.modules.localization.Messages;
31 import org.scilab.modules.types.ScilabTList;
32 import org.scilab.modules.xcos.Xcos;
33 import org.scilab.modules.xcos.block.BasicBlock;
34 import org.scilab.modules.xcos.graph.XcosDiagram;
35 import org.scilab.modules.xcos.io.scicos.ScicosFormatException;
36 import org.scilab.modules.xcos.io.scicos.ScilabDirectHandler;
37 import org.scilab.modules.xcos.palette.model.Category;
38 import org.scilab.modules.xcos.palette.model.PaletteBlock;
39 import org.scilab.modules.xcos.palette.model.PaletteNode;
40 import org.scilab.modules.xcos.palette.model.PreLoaded;
41 import org.scilab.modules.xcos.utils.BlockPositioning;
42 import org.scilab.modules.xcos.utils.XcosConstants;
44 import com.mxgraph.swing.mxGraphComponent;
45 import com.mxgraph.util.mxCellRenderer;
46 import com.mxgraph.util.mxRectangle;
47 import com.mxgraph.view.mxGraphView;
48 import com.mxgraph.view.mxStylesheet;
51 * Utility class which is the entry point from Scilab for palette related
54 public final class Palette {
55 /** the "name" argument */
56 public static final String NAME = "name";
58 /** Error message used on invalid path */
59 public static final String WRONG_INPUT_ARGUMENT_S_INVALID_TREE_PATH = Messages.gettext("Wrong input argument \"%s\": invalid tree path.\n");
60 /** Error message used on invalid node */
61 public static final String WRONG_INPUT_ARGUMENT_S_INVALID_NODE = Messages
62 .gettext("Wrong input argument \"%s\": invalid node, use 'xcosPalDisable' instead.\n");
63 /** "Unable to import" string */
64 public static final String UNABLE_TO_IMPORT = Messages.gettext("Unable to import %s .\n");
66 private static final Logger LOG = Logger.getLogger(Palette.class.getName());
67 private static final String XCOS = "xcos";
68 private static final String PALETTE_GIWS_XML = "Palette.giws.xml";
71 * Default hidden constructor
77 * Get the {@link PaletteNode} of the path.
82 * if <code>true</code> the Category path will be created,
83 * otherwise it will not.
84 * @return the selected node
86 private static PaletteNode getPathNode(final String[] path, final boolean create) {
88 if (!SwingUtilities.isEventDispatchThread()) {
89 throw new RuntimeException("Unable to manipulate palette outside the EDT thread.");
92 Category node = PaletteManager.getInstance().getRoot();
94 if (path == null || path.length == 0 || (path.length == 1 && path[0].isEmpty())) {
98 for (int categoryCounter = 0; categoryCounter < path.length; categoryCounter++) {
100 for (final PaletteNode next : node.getNode()) {
101 if (next.getName().equals(path[categoryCounter]) && next instanceof Category) {
102 node = (Category) next;
104 } else if (next.getName().equals(path[categoryCounter]) && (categoryCounter == path.length - 1)) {
105 return next; // found the terminal Palette instance
109 if (!node.toString().equals(path[categoryCounter])) {
111 final Category cat = new Category();
112 cat.setName(path[categoryCounter]);
113 cat.setEnable(create);
116 node.getNode().add(cat);
128 * Load an xcos palette into the palette manager
131 * the scilab exported palette variable name
133 * TreePath of the palette
134 * @throws JavasciException
135 * on invocation error
137 @ScilabExported(module = XCOS, filename = PALETTE_GIWS_XML)
138 public static void loadPal(final String name, final String[] category) throws JavasciException {
140 * If the env. is headless only perform fake loading to assert data
143 if (GraphicsEnvironment.isHeadless()) {
144 LOG.warning("Headless environment detected, only perform sanity check");
145 loadPalHeadless(name);
152 final ScilabTList data = (ScilabTList) Scilab.getInCurrentScilabSession(name);
155 * handle shared data on the EDT thread
158 SwingUtilities.invokeAndWait(new Runnable() {
163 * Decode the style part of the palette
165 final mxStylesheet styleSheet = Xcos.getInstance().getStyleSheet();
167 new StyleElement().decode(data, styleSheet);
168 } catch (final ScicosFormatException e) {
169 throw new RuntimeException(e);
172 // reload all the opened diagram (clear states)
173 for (final XcosDiagram d : Xcos.getInstance().openedDiagrams()) {
175 final mxGraphView view = d.getView();
180 final mxGraphComponent comp = d.getAsComponent();
187 final PaletteNode node = getPathNode(category, true);
188 if (!(node instanceof Category)) {
189 throw new RuntimeException(String.format(WRONG_INPUT_ARGUMENT_S_INVALID_TREE_PATH, "category"));
191 final Category cat = (Category) node;
194 * Adding the palette tree part of the palette
198 pal = new PreLoadedElement().decode(data, new PreLoaded.Dynamic());
199 } catch (final ScicosFormatException e) {
200 throw new RuntimeException(e);
202 cat.getNode().add(pal);
205 PaletteNode.refreshView(pal);
206 } catch (Exception e) {
211 } catch (final InterruptedException e) {
212 LOG.severe(e.toString());
213 } catch (final InvocationTargetException e) {
214 Throwable throwable = e;
215 String firstMessage = null;
216 while (throwable != null) {
217 firstMessage = throwable.getLocalizedMessage();
218 throwable = throwable.getCause();
221 throw new RuntimeException(firstMessage, e);
225 private static final void loadPalHeadless(final String name) throws JavasciException {
227 final ScilabTList data = (ScilabTList) Scilab.getInCurrentScilabSession(name);
230 new StyleElement().decode(data, new mxStylesheet());
232 // palette data check
233 new PreLoadedElement().decode(data, new PreLoaded.Dynamic());
235 } catch (ScicosFormatException e) {
236 throw new RuntimeException(e);
241 * Load an xcos palette into the palette manager at the root category.
244 * the scilab exported palette variable name
245 * @throws JavasciException
246 * on invocation error
248 @ScilabExported(module = XCOS, filename = PALETTE_GIWS_XML)
249 public static void loadPal(final String name) throws JavasciException {
254 * Add a category into the palette manager
257 * TreePath of the palette
259 * default visibility of the palette
261 @ScilabExported(module = XCOS, filename = PALETTE_GIWS_XML)
262 public static void addCategory(final String[] name, final boolean visible) {
264 SwingUtilities.invokeAndWait(new Runnable() {
267 final PaletteNode node = getPathNode(name, true);
268 if (node instanceof Category) {
269 node.setEnable(visible);
271 throw new RuntimeException(String.format(WRONG_INPUT_ARGUMENT_S_INVALID_TREE_PATH, NAME));
274 PaletteNode.refreshView(node.getParent());
277 } catch (final InterruptedException e) {
278 Logger.getLogger(Palette.class.getName()).severe(e.toString());
279 } catch (final InvocationTargetException e) {
280 Throwable throwable = e;
281 String firstMessage = null;
282 while (throwable != null) {
283 firstMessage = throwable.getLocalizedMessage();
284 throwable = throwable.getCause();
287 throw new RuntimeException(firstMessage, e);
292 * Remove a palette or a category of the palette manager
295 * TreePath of the palette
297 @ScilabExported(module = XCOS, filename = PALETTE_GIWS_XML)
298 public static void remove(final String[] name) {
300 SwingUtilities.invokeAndWait(new Runnable() {
303 final PaletteNode node = getPathNode(name, false);
304 PaletteNode.remove(node);
307 } catch (final InterruptedException e) {
308 LOG.severe(e.toString());
309 } catch (final InvocationTargetException e) {
310 Throwable throwable = e;
311 String firstMessage = null;
312 while (throwable != null) {
313 firstMessage = throwable.getLocalizedMessage();
314 throwable = throwable.getCause();
317 throw new RuntimeException(firstMessage, e);
322 * Remove a palette or a category of the palette manager
325 * TreePath of the palette or category
327 * True to set the palette visible, false otherwise
329 @ScilabExported(module = XCOS, filename = PALETTE_GIWS_XML)
330 public static void enable(final String[] name, final boolean status) {
332 SwingUtilities.invokeAndWait(new Runnable() {
335 final PaletteNode node = getPathNode(name, false);
337 throw new RuntimeException(String.format(WRONG_INPUT_ARGUMENT_S_INVALID_TREE_PATH, NAME));
340 node.setEnable(status);
342 PaletteNode.refreshView(node.getParent());
345 } catch (final InterruptedException e) {
346 LOG.severe(e.toString());
347 } catch (final InvocationTargetException e) {
348 Throwable throwable = e;
349 String firstMessage = null;
350 while (throwable != null) {
351 firstMessage = throwable.getLocalizedMessage();
352 throwable = throwable.getCause();
355 throw new RuntimeException(firstMessage, e);
360 * Move a palette or a category of the palette manager
363 * TreePath of the palette or category
365 * TreePath of the destination
367 @ScilabExported(module = XCOS, filename = PALETTE_GIWS_XML)
368 public static void move(final String[] source, final String[] target) {
370 SwingUtilities.invokeAndWait(new Runnable() {
374 final PaletteNode src = getPathNode(source, false);
376 throw new RuntimeException(String.format(WRONG_INPUT_ARGUMENT_S_INVALID_TREE_PATH, "source"));
379 final PaletteNode trg = getPathNode(target, true);
380 if (trg == null || !(trg instanceof Category)) {
381 throw new RuntimeException(String.format(WRONG_INPUT_ARGUMENT_S_INVALID_TREE_PATH, "target"));
383 final Category destination = (Category) trg;
385 final Category[] toBeReloaded = new Category[] { src.getParent(), destination };
387 if (toBeReloaded[0] != null) {
388 toBeReloaded[0].getNode().remove(src);
390 destination.getNode().add(src);
391 src.setParent(destination);
393 PaletteNode.refreshView(toBeReloaded[0]);
394 PaletteNode.refreshView(toBeReloaded[1]);
397 } catch (final InterruptedException e) {
398 LOG.severe(e.toString());
399 } catch (final InvocationTargetException e) {
400 Throwable throwable = e;
401 String firstMessage = null;
402 while (throwable != null) {
403 firstMessage = throwable.getLocalizedMessage();
404 throwable = throwable.getCause();
407 throw new RuntimeException(firstMessage, e);
412 * Generate a palette block image from a block instance stored into scilab
413 * (need a valid style).
416 * the output file path use to save the palette block.
420 @ScilabExported(module = XCOS, filename = PALETTE_GIWS_XML)
421 public static void generatePaletteIcon(final String iconPath) throws Exception {
423 * If the env. is headless does nothing
425 if (GraphicsEnvironment.isHeadless()) {
426 LOG.warning("Headless environment detected, do not generate icons");
430 final ScilabDirectHandler handler = ScilabDirectHandler.acquire();
432 final BasicBlock block = handler.readBlock();
434 generateIcon(block, iconPath);
439 if (LOG.isLoggable(Level.FINEST)) {
440 LOG.finest(iconPath + " updated.");
444 private static void generateIcon(BasicBlock block, final String iconPath) throws IOException {
445 if (block == null || block.getGeometry() == null) {
448 block.getGeometry().setX(XcosConstants.PALETTE_BLOCK_WIDTH);
449 block.getGeometry().setY(XcosConstants.PALETTE_BLOCK_HEIGHT);
451 final XcosDiagram graph = new XcosDiagram();
452 graph.installListeners();
454 graph.addCell(block);
457 BlockPositioning.updateBlockView(block);
462 final mxGraphComponent graphComponent = graph.getAsComponent();
463 graphComponent.refresh();
465 final mxRectangle bounds = graph.getPaintBounds(new Object[] { block });
466 final double width = bounds.getWidth();
467 final double height = bounds.getHeight();
470 if (width > XcosConstants.PALETTE_BLOCK_WIDTH || height > XcosConstants.PALETTE_BLOCK_HEIGHT) {
471 scale = Math.min(XcosConstants.PALETTE_BLOCK_WIDTH / width, XcosConstants.PALETTE_BLOCK_HEIGHT / height) / XcosConstants.PALETTE_BLOCK_ICON_RATIO;
476 final BufferedImage image = mxCellRenderer.createBufferedImage(graph, null, scale, graphComponent.getBackground(), graphComponent.isAntiAlias(), null,
477 graphComponent.getCanvas());
479 final String extension = iconPath.substring(iconPath.lastIndexOf('.') + 1);
480 ImageIO.write(image, extension, new File(iconPath));
484 * Helper function used to regenerate palette block images from block
487 * This method is not export to scilab but is needed for internal purpose
488 * (palette block image generation)
490 public static void generateAllPaletteImages() {
491 SwingUtilities.invokeLater(new Runnable() {
494 final PaletteManager current = PaletteManager.getInstance();
495 iterate(current.getRoot().getNode());
496 current.saveConfig();
499 private void iterate(final List<PaletteNode> children) {
500 for (final PaletteNode next : children) {
501 if (next instanceof Category) {
502 iterate(((Category) next).getNode());
503 } else if (next instanceof PreLoaded) {
504 generatePreLoadedIcon((PreLoaded) next);
510 * Generate PreLoaded icon file.
515 private void generatePreLoadedIcon(final PreLoaded current) {
516 final List<PaletteBlock> blocks = current.getBlock();
517 for (final PaletteBlock paletteBlock : blocks) {
520 final BasicBlock block = new PaletteBlockCtrl(paletteBlock).loadBlock();
521 generateIcon(block, paletteBlock.getIcon().getEvaluatedPath());
522 } catch (IOException e) {
524 } catch (ScicosFormatException e) {
531 LOG.finest("All images has been generated.");