diff options
author | Shashank | 2017-05-29 12:40:26 +0530 |
---|---|---|
committer | Shashank | 2017-05-29 12:40:26 +0530 |
commit | 0345245e860375a32c9a437c4a9d9cae807134e9 (patch) | |
tree | ad51ecbfa7bcd3cc5f09834f1bb8c08feaa526a4 /modules/graph/src/java | |
download | scilab_for_xcos_on_cloud-0345245e860375a32c9a437c4a9d9cae807134e9.tar.gz scilab_for_xcos_on_cloud-0345245e860375a32c9a437c4a9d9cae807134e9.tar.bz2 scilab_for_xcos_on_cloud-0345245e860375a32c9a437c4a9d9cae807134e9.zip |
CMSCOPE changed
Diffstat (limited to 'modules/graph/src/java')
48 files changed, 6457 insertions, 0 deletions
diff --git a/modules/graph/src/java/org/scilab/modules/graph/ScilabCanvas.java b/modules/graph/src/java/org/scilab/modules/graph/ScilabCanvas.java new file mode 100755 index 000000000..72979f46b --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/ScilabCanvas.java @@ -0,0 +1,343 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.geom.Dimension2D; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Map; +import java.util.logging.Logger; + +import org.apache.batik.gvt.GraphicsNode; +import org.scilab.forge.jlatexmath.TeXFormula; +import org.scilab.modules.graph.shape.LatexTextShape; +import org.scilab.modules.graph.shape.MathMLTextShape; +import org.scilab.modules.graph.shape.SvgShape; +import org.scilab.modules.graph.utils.MathMLRenderUtils; +import org.scilab.modules.graph.utils.ScilabGraphConstants; +import org.scilab.modules.graph.utils.ScilabGraphUtils; +import org.scilab.modules.graph.view.SupportedLabelType; +import org.xml.sax.SAXException; + +import com.mxgraph.canvas.mxGraphics2DCanvas; +import com.mxgraph.shape.mxITextShape; +import com.mxgraph.swing.view.mxInteractiveCanvas; +import com.mxgraph.util.mxConstants; +import com.mxgraph.util.mxRectangle; +import com.mxgraph.util.mxUtils; +import com.mxgraph.view.mxCellState; + +/** + * Painter for each vertex and edge + * + * This is tightly coupled to jgraphx internals. + */ +public class ScilabCanvas extends mxInteractiveCanvas { + + /** The rotation step of the clockwise and anticlockwise rotation */ + public static final int ROTATION_STEP = 90; + /** The max valid rotation value (always 360 degres) */ + public static final int MAX_ROTATION = 360; + + /** The border ratio between the background image and the icon image */ + private static final double BORDER_RATIO = 0.9; + + static { + putShape(mxConstants.SHAPE_LABEL, new SvgShape()); + putTextShape(SupportedLabelType.Latex.name(), new LatexTextShape()); + putTextShape(SupportedLabelType.MathML.name(), new MathMLTextShape()); + } + + private URL urlBasePath; + + /** Default constructor */ + public ScilabCanvas() { + } + + /** + * Get the text shape associated with the text + * + * @param text + * the associated text + * @param style + * the current style + * @param html + * true, if the text is html formatted, false otherwise. + * @return the associated text shape + */ + public mxITextShape getTextShape(String text, Map<String, Object> style, boolean html) { + final mxITextShape ret; + + final SupportedLabelType type; + if (html) { + type = SupportedLabelType.getFromHTML(text); + } else { + type = SupportedLabelType.getFromText(text); + } + + switch (type) { + case Latex: + try { + // parse the text and cache it if valid. Will throw an exception + // if the text is not valid. + new TeXFormula(SupportedLabelType.Latex.escape(text)); + + ret = textShapes.get(type.name()); + } catch (RuntimeException e) { + return super.getTextShape(style, html); + } + break; + + case MathML: + try { + // parse the text and cache it if valid. Will throw an exception + // if the text is not valid. + MathMLRenderUtils.getMathMLComponent(text); + + ret = textShapes.get(type.name()); + } catch (SAXException e) { + return super.getTextShape(style, html); + } + break; + + default: + ret = super.getTextShape(style, html); + break; + } + + return ret; + } + + /** + * Scale the graphic context depending on the "flip and "mirror" properties + * + * @param temporaryGraphics + * the current graphic surface + * @param style + * Style contents + * @param bounds + * the current bounds + */ + private void applyFlipAndMirror(Graphics2D temporaryGraphics, Map<String, Object> style, mxRectangle bounds) { + if (bounds == null) { + return; + } + + final boolean flip = mxUtils.isTrue(style, ScilabGraphConstants.STYLE_FLIP, false); + final boolean mirror = mxUtils.isTrue(style, ScilabGraphConstants.STYLE_MIRROR, false); + + final double x = bounds.getCenterX(); + final double y = bounds.getCenterY(); + + temporaryGraphics.translate(x, y); + + // scale, 1st flip, 2nd mirror + // The scale operation concatenate AffineTransforms. + + if (flip) { + temporaryGraphics.scale(1.0, -1.0); + } + + if (mirror) { + temporaryGraphics.scale(-1.0, 1.0); + } + + temporaryGraphics.translate(-x, -y); + } + + /** + * Allocate a new graphic surface and set some properties on it. + * + * This method handle the flip and the mirror properties. + * + * @param style + * the current style + * @param opacity + * the opacity + * @param bounds + * the bounds + * @return a graphic surface + * @see com.mxgraph.canvas.mxGraphics2DCanvas#createTemporaryGraphics(java.util.Map, + * float, com.mxgraph.util.mxRectangle) + */ + @Override + public Graphics2D createTemporaryGraphics(Map<String, Object> style, float opacity, mxRectangle bounds) { + Graphics2D temporaryGraphics = super.createTemporaryGraphics(style, opacity, bounds); + + applyFlipAndMirror(temporaryGraphics, style, bounds); + + return temporaryGraphics; + } + + /** + * Draw the text label on the cell state. + * + * This method is extracted from {@link mxGraphics2DCanvas} to add a text + * argument to {@link #getTextShape(Map, boolean)}. + * + * @param text + * the current text + * @param state + * the cell state + * @param html + * true, if the text may be HTML, false otherwise. + * @return the associated shape + * @see com.mxgraph.canvas.mxGraphics2DCanvas#drawLabel(java.lang.String, + * com.mxgraph.view.mxCellState, boolean) + */ + @Override + public Object drawLabel(String text, mxCellState state, boolean html) { + Map<String, Object> style = state.getStyle(); + mxITextShape shape = getTextShape(text, style, html); + + if (g != null && shape != null && drawLabels && text != null && text.length() > 0) { + // Creates a temporary graphics instance for drawing this shape + float opacity = mxUtils.getFloat(style, mxConstants.STYLE_TEXT_OPACITY, 100); + Graphics2D previousGraphics = g; + g = createTemporaryGraphics(style, opacity, null); + + // Draws the label background and border + Color bg = mxUtils.getColor(style, mxConstants.STYLE_LABEL_BACKGROUNDCOLOR); + Color border = mxUtils.getColor(style, mxConstants.STYLE_LABEL_BORDERCOLOR); + paintRectangle(state.getLabelBounds().getRectangle(), bg, border); + + // Paints the label and restores the graphics object + shape.paintShape(this, text, state, style); + g.dispose(); + g = previousGraphics; + } + + return shape; + } + + /** + * Paint the foreground image. + * + * This method paint an iso-scaled and centered image. + * + * @param w + * the width + * @param h + * the height + * @param image + * the current image + */ + public void paintSvgForegroundImage(int w, int h, String image) { + /* + * Fetch SVG file representation + */ + URL url; + try { + url = new URL(image); + } catch (MalformedURLException e) { + Logger.getLogger(ScilabCanvas.class.getName()).severe(e.toString()); + return; + } + GraphicsNode icon = ScilabGraphUtils.getSVGComponent(url); + + if (icon == null || icon.getBounds() == null) { + return; + } + + /* + * Perform calculations + */ + + // Iso scale to the bounds - border size + Dimension2D bounds = ScilabGraphUtils.getSVGDocumentSizes(url); + + // Calculating icon bordered bounds + final double ih = bounds.getHeight(); + final double iw = bounds.getWidth(); + + // Calculate per axis scaling factor + final double shFactor = h / ih; + final double swFactor = w / iw; + + // Calculate the default ratio (iso scaling) + double ratio; + if (shFactor > swFactor) { + ratio = swFactor; + } else { + ratio = shFactor; + } + + // Adding borders + ratio *= BORDER_RATIO; + + // Calculate scaled height and width + final double sh = ratio * ih; + final double sw = ratio * iw; + + // Center the image on the block + double tx = (w - sw) / 2; + double ty = (h - sh) / 2; + + /* + * Everything has been calculated, render now. + */ + + // Translate from base point to centered base point + g.translate(tx, ty); + + // scale to the ratio + g.scale(ratio, ratio); + + // Paint + icon.paint(g); + } + + /** + * Set the image path and store the path as a URL. + * + * @param imageBasePath + * the new path + * @see com.mxgraph.canvas.mxBasicCanvas#setImageBasePath(java.lang.String) + */ + @Override + public void setImageBasePath(String imageBasePath) { + super.setImageBasePath(imageBasePath); + + try { + this.urlBasePath = new URL(imageBasePath); + } catch (MalformedURLException e) { + Logger.getLogger(ScilabCanvas.class.getName()).severe(e.toString()); + } + } + + /** + * Gets the image path from the given style. If the path is relative (does + * not start with a slash) then it is appended to the imageBasePath. + * + * @param style + * the current style + * @return the image path + */ + @Override + public String getImageForStyle(Map<String, Object> style) { + String filename = mxUtils.getString(style, mxConstants.STYLE_IMAGE); + + if (filename == null) { + return null; + } + + try { + return new URL(this.urlBasePath, filename).toExternalForm(); + } catch (MalformedURLException e) { + } + + return null; + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/ScilabComponent.java b/modules/graph/src/java/org/scilab/modules/graph/ScilabComponent.java new file mode 100755 index 000000000..b8dc823b8 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/ScilabComponent.java @@ -0,0 +1,240 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.GraphicsEnvironment; +import java.awt.event.MouseEvent; + +import com.mxgraph.model.mxICell; +import com.mxgraph.model.mxIGraphModel; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.util.mxEvent; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.util.mxEventSource; +import com.mxgraph.util.mxRectangle; +import com.mxgraph.view.mxCellState; +import com.mxgraph.view.mxGraphView; + +/** + * Implement the default component for the {@link ScilabGraph}. + */ +@SuppressWarnings(value = { "serial" }) +public class ScilabComponent extends mxGraphComponent { + /** + * Color use to mask the graph when the graph is locked + */ + private static final Color MASK_COLOR = new Color(240, 240, 240, 100); + + private static final double SCALE_MULTIPLIER = 1.1; + + /** + * Construct the component with the associated graph + * + * @param graph + * The associated graph + */ + public ScilabComponent(ScilabGraph graph) { + super(graph); + } + + /** + * @return the associated graph control + * @see com.mxgraph.swing.mxGraphComponent#createGraphControl() + */ + @Override + protected mxGraphControl createGraphControl() { + return new ScilabGraphControl(); + } + + /** + * Create the associated canvas + * + * @return the canvas + */ + @Override + public ScilabCanvas createCanvas() { + return new ScilabCanvas(); + } + + /** + * Zoom the whole graph and center the view on it. + * + * @param cells + * the cells to center on + */ + public void zoomAndCenterToCells(final Object[] cells) { + final mxRectangle preference = zoomBounds(cells); + final Dimension actual = getViewport().getSize(); + + final double newScale; + final double heightScale = actual.getHeight() / preference.getHeight(); + final double widthScale = actual.getWidth() / preference.getWidth(); + + if (heightScale > 1.0) { + if (widthScale > 1.0) { + // We need to zoom in (the max applicable zoom is the lowest) + newScale = Math.min(heightScale, widthScale); + } else { + // we need to zoom out (only widthScale is < 1.0) + newScale = widthScale; + } + } else { + if (widthScale > 1.0) { + // we need to zoom out (only heightScale is < 1.0) + newScale = heightScale; + } else { + // We need to zoom out (the max applicable zoom is the lowest) + newScale = Math.min(heightScale, widthScale); + } + } + + // do not apply small zoom values + if (Math.abs(1.0 - newScale) < 0.2) { + getGraphControl().scrollRectToVisible(zoomBounds(cells).getRectangle(), true); + return; + } + + zoom(newScale / SCALE_MULTIPLIER); + getGraphControl().scrollRectToVisible(zoomBounds(cells).getRectangle(), true); + } + + private final mxRectangle zoomBounds(final Object[] cells) { + final mxRectangle preference; + final Object[] c; + if (cells == null || cells.length == 0) { + c = graph.getChildCells(graph.getDefaultParent()); + } else { + c = cells; + } + preference = getChildrenBounds(c); + + return preference; + } + + /** + * Get the children bound for the cells + * + * @param cells + * the root of the graph + * @return the rectangle or null if not applicable + */ + private mxRectangle getChildrenBounds(final Object[] cells) { + mxRectangle result = null; + + if (cells != null && cells.length > 0) { + final mxGraphView view = graph.getView(); + final mxIGraphModel model = graph.getModel(); + + for (int i = 0; i < cells.length; i++) { + if (model.isVertex(cells[i]) || model.isEdge(cells[i])) { + final mxICell parent = ((mxICell) cells[i]); + final int childCount = parent.getChildCount(); + + for (int j = 0; j < childCount; j++) { + final mxICell child = parent.getChildAt(j); + + result = updateRectangle(result, view, child); + } + + result = updateRectangle(result, view, parent); + } + } + } + + return result; + } + + /** + * Update the rectangle parameter with the cell status + * + * @param result + * the previous result + * @param view + * the current view + * @param child + * the child we have to work on + * @return the updated rectangle + */ + private mxRectangle updateRectangle(mxRectangle result, final mxGraphView view, final mxICell child) { + final mxCellState state = view.getState(child); + mxRectangle rect = result; + + if (state != null) { + if (rect == null) { + rect = new mxRectangle(state); + } else { + rect.add(state); + } + } + return rect; + } + + /** + * Implement a graph control which paint a foreground on top of the view + * when the graph is locked. + */ + @SuppressWarnings(value = { "serial" }) + public class ScilabGraphControl extends mxGraphControl { + + /** + * Default constructor + */ + public ScilabGraphControl() { + super(); + + // Paint the foreground color after the real paint + addListener(mxEvent.AFTER_PAINT, new mxEventSource.mxIEventListener() { + @Override + public void invoke(Object sender, mxEventObject evt) { + + Graphics g = (Graphics) evt.getProperty("g"); + if (getGraph().isCellsLocked()) { + g.setColor(MASK_COLOR); + + Dimension b = getGraphControl().getSize(); + + g.fillRect(0, 0, b.width, b.height); + } + } + }); + } + + /** + * @see javax.swing.JComponent#processMouseMotionEvent(java.awt.event.MouseEvent) + * + * Overloaded to filter out any cursor update if the graph is locked + */ + @Override + protected void processMouseMotionEvent(MouseEvent e) { + if (!getGraph().isCellsLocked()) { + super.processMouseMotionEvent(e); + } + } + } + + /* + * Disable some handlers in case of an headless env. + */ + + @Override + protected void createHandlers() { + if (GraphicsEnvironment.isHeadless()) { + return; + } + + super.createHandlers(); + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/ScilabGraph.java b/modules/graph/src/java/org/scilab/modules/graph/ScilabGraph.java new file mode 100755 index 000000000..3fefbd6f1 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/ScilabGraph.java @@ -0,0 +1,388 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Bruno JOFRET + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.scilab.modules.graph.utils.ScilabGraphConstants; +import org.scilab.modules.graph.utils.ScilabGraphMessages; +import org.scilab.modules.graph.view.ScilabGraphView; + +import com.mxgraph.model.mxGraphModel; +import com.mxgraph.model.mxGraphModel.mxChildChange; +import com.mxgraph.model.mxGraphModel.mxCollapseChange; +import com.mxgraph.model.mxGraphModel.mxGeometryChange; +import com.mxgraph.model.mxGraphModel.mxStyleChange; +import com.mxgraph.model.mxGraphModel.mxTerminalChange; +import com.mxgraph.model.mxGraphModel.mxValueChange; +import com.mxgraph.model.mxGraphModel.mxVisibleChange; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.handler.mxRubberband; +import com.mxgraph.swing.util.mxGraphActions; +import com.mxgraph.util.mxEvent; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.util.mxUndoManager; +import com.mxgraph.util.mxUndoableEdit; +import com.mxgraph.util.mxUndoableEdit.mxUndoableChange; +import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxGraphView; + +/** + * Represent the base diagram of Xcos. + * + * It performs generic operations like undo/redo management, action clean-up, + * modification state management, SwingScilabTab association, etc... + */ +public class ScilabGraph extends mxGraph { + /** + * The default component of a scilab graph + */ + private ScilabComponent component; + + private final mxUndoManager undoManager = new mxUndoManager(); + + private String title = null; + private File savedFile; + private boolean modified; + private boolean opened; + private boolean readOnly; + + private transient mxRubberband rubberBand; + + private transient String graphTab; + private transient String viewPortTab; + + /** + * Manage the modification state on change + */ + private final mxIEventListener changeTracker = new mxIEventListener() { + @Override + public void invoke(Object source, mxEventObject evt) { + setModified(true); + } + }; + + /** + * Manage the undo/redo on change + */ + private final mxIEventListener undoHandler = new mxIEventListener() { + @Override + public void invoke(Object source, mxEventObject evt) { + undoManager.undoableEditHappened((mxUndoableEdit) evt.getProperty(ScilabGraphConstants.EVENT_CHANGE_EDIT)); + } + }; + + /** + * Remove the undo handler from the component + */ + public void removeUndoHandler() { + getModel().removeListener(undoHandler, mxEvent.UNDO); + } + + /** + * Register the undo handler on the right component + */ + public void registerUndoHandler() { + // Undo / Redo capabilities + getModel().addListener(mxEvent.UNDO, undoHandler); + } + + /** + * Update the selection on undo/redo + */ + private final mxIEventListener selectionHandler = new mxIEventListener() { + @Override + public void invoke(Object source, mxEventObject evt) { + List<mxUndoableChange> changes = ((mxUndoableEdit) evt.getProperty(ScilabGraphConstants.EVENT_CHANGE_EDIT)).getChanges(); + getSelectionModel().setCells(getSelectionCellsForChanges(changes)); + } + }; + + /** + * Update the component when the graph is locked + */ + private final PropertyChangeListener cellLockBackgroundUpdater = new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals("cellsLocked")) { + getAsComponent().getGraphControl().repaint(); + } + } + }; + + /** + * /** Default constructor: - disable unused actions - install listeners - + * Replace JGraphX components by specialized components if needed. + */ + public ScilabGraph() { + super(); + + // Disabling the default connected action and event listeners. + mxGraphActions.getSelectNextAction().setEnabled(false); + mxGraphActions.getSelectPreviousAction().setEnabled(false); + mxGraphActions.getSelectChildAction().setEnabled(false); + mxGraphActions.getSelectParentAction().setEnabled(false); + + registerUndoHandler(); + + // Keeps the selection in sync with the command history + undoManager.addListener(mxEvent.UNDO, selectionHandler); + undoManager.addListener(mxEvent.REDO, selectionHandler); + + setComponent(new ScilabComponent(this)); + + // graph locked change support + changeSupport.addPropertyChangeListener(cellLockBackgroundUpdater); + + // Modified property change + getModel().addListener(mxEvent.CHANGE, changeTracker); + } + + /** + * @return The previously saved file or null. + */ + public File getSavedFile() { + return savedFile; + } + + /** + * @param savedFile + * The new saved file + */ + public void setSavedFile(final File savedFile) { + this.savedFile = savedFile; + + // register the saved dir as the image base path (for relative images + // location). + if (savedFile != null && savedFile.getParentFile() != null) { + getAsComponent().getCanvas().setImageBasePath(savedFile.getParentFile().toURI().toASCIIString()); + } + } + + /** + * @return true, if the graph has been modified ; false otherwise. + */ + public boolean isModified() { + return modified; + } + + /** + * Modify the state of the diagram. + * + * @param modified + * The new modified state. + * @category UseEvent + */ + public void setModified(boolean modified) { + boolean oldValue = this.modified; + this.modified = modified; + + if (getAsComponent() != null) { + getAsComponent().firePropertyChange("modified", oldValue, modified); + } + } + + /** + * @param title + * The new title of the tab + */ + public void setTitle(String title) { + this.title = title; + } + + /** + * @return The current Tab title + */ + public String getTitle() { + if (title == null) { + final Date d = Calendar.getInstance().getTime(); + final String time = DateFormat.getTimeInstance().format(d); + title = String.format(ScilabGraphMessages.UNTITLED, time); + } + return title; + } + + /** + * Get the graph tab uuid + * + * @return + */ + public String getGraphTab() { + return graphTab; + } + + /** + * Set the graph tab uuid + * + * @param uuid + * the diagram tab + */ + public void setGraphTab(String uuid) { + this.graphTab = uuid; + } + + /** + * Get the view port tab uuid + * + * @return the view port tab + */ + public String getViewPortTab() { + return viewPortTab; + } + + /** + * Set the view port tab uuid + * + * @param uuid + * the view port tab + */ + public void setViewPortTab(String uuid) { + this.viewPortTab = uuid; + } + + /** + * @return The component associated with the current graph. + */ + public mxGraphComponent getAsComponent() { + return component; + } + + /** + * @param component + * The graphical component associated with this graph + */ + public void setComponent(ScilabComponent component) { + this.component = component; + + if (component != null) { + // Adds rubberband selection + rubberBand = new mxRubberband(component); + } else { + rubberBand = null; + } + } + + /** + * The instance can be not visible but used (when using SuperBlock). The + * openned flag is true in this case and also when the Window/Tab is + * visible. + * + * @param opened + * Openned state + */ + public void setOpened(boolean opened) { + this.opened = opened; + } + + /** + * @return Openned state + */ + public boolean isOpened() { + return opened; + } + + /** + * A read-only state will disable all actions in the graph. + * + * @param readOnly + * Read-only state + */ + public void setReadOnly(boolean readOnly) { + this.readOnly = readOnly; + + setCellsLocked(readOnly); + } + + /** + * @return True if actions are not allowed, false otherwise. + */ + public boolean isReadonly() { + return readOnly; + } + + /** + * @return The associated RubberBand + * @see com.mxgraph.swing.handler.mxRubberband + */ + public mxRubberband getRubberBand() { + return rubberBand; + } + + /** + * @return The undo manager associated with this graph + */ + public final mxUndoManager getUndoManager() { + return undoManager; + } + + /** + * @return the newly allocated graph + * @see com.mxgraph.view.mxGraph#createGraphView() + */ + @Override + protected mxGraphView createGraphView() { + return new ScilabGraphView(this); + } + + /* + * Utils + */ + /** + * Returns the cells to be selected for the given list of changes. + * + * @param changes + * the changes + * @param model + * the model to work on + * @return the cells + */ + public static Object[] getSelectionCellsForChanges(final List<mxUndoableChange> changes, final mxGraphModel model) { + List<Object> cells = new ArrayList<Object>(); + Iterator<mxUndoableChange> it = changes.iterator(); + + while (it.hasNext()) { + Object change = it.next(); + + if (change instanceof mxChildChange) { + cells.add(((mxChildChange) change).getChild()); + } else if (change instanceof mxTerminalChange) { + cells.add(((mxTerminalChange) change).getCell()); + } else if (change instanceof mxValueChange) { + cells.add(((mxValueChange) change).getCell()); + } else if (change instanceof mxStyleChange) { + cells.add(((mxStyleChange) change).getCell()); + } else if (change instanceof mxGeometryChange) { + cells.add(((mxGeometryChange) change).getCell()); + } else if (change instanceof mxCollapseChange) { + cells.add(((mxCollapseChange) change).getCell()); + } else if (change instanceof mxVisibleChange) { + mxVisibleChange vc = (mxVisibleChange) change; + + if (vc.isVisible()) { + cells.add(((mxVisibleChange) change).getCell()); + } + } + } + + return mxGraphModel.getTopmostCells(model, cells.toArray()); + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/ScilabGraphUniqueObject.java b/modules/graph/src/java/org/scilab/modules/graph/ScilabGraphUniqueObject.java new file mode 100755 index 000000000..7e54b9ef7 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/ScilabGraphUniqueObject.java @@ -0,0 +1,85 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Bruno JOFRET + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph; + +import java.awt.geom.Point2D; +import java.io.Serializable; +import java.rmi.server.UID; + +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGeometry; + +/** + * Implement a unique object. + * + * All cells in a ScilabGraph must follow this signature in order to be unique + * in the graph. + */ +public abstract class ScilabGraphUniqueObject extends mxCell implements Comparable<ScilabGraphUniqueObject>, Serializable { + + private static final long serialVersionUID = -2915277403393545917L; + + /** + * Constructor + */ + public ScilabGraphUniqueObject() { + super(); + generateId(); + } + + /** + * Generated a new id and set it to the current cell. + * + * @see com.mxgraph.model.mxCell#setId(java.lang.String) + */ + public void generateId() { + super.setId(new UID().toString()); + } + + /** + * Compare this object with another one. + * + * @param o + * the object to compare to. + * @return True if instance are the same; False otherwise. + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + @Override + public int compareTo(ScilabGraphUniqueObject o) { + mxGeometry geom1 = getGeometry(); + mxGeometry geom2 = o.getGeometry(); + + final Point2D.Double p1 = new Point2D.Double(geom1.getCenterX(), geom1.getCenterY()); + final Point2D.Double p2 = new Point2D.Double(geom2.getCenterX(), geom2.getCenterY()); + + return (int) p1.distanceSq(p2); + } + + /** + * @return a clone of the cell. + * @throws CloneNotSupportedException + * Thrown to indicate that the clone method in class Object has + * been called to clone an object, but that the object's class + * does not implement the Cloneable interface. + * @see com.mxgraph.model.mxCell#clone() + */ + @Override + public Object clone() throws CloneNotSupportedException { + ScilabGraphUniqueObject clone = (ScilabGraphUniqueObject) super.clone(); + + /* regenerate a new id for the clone */ + clone.generateId(); + + return clone; + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/CopyAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/CopyAction.java new file mode 100755 index 000000000..50d70668c --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/CopyAction.java @@ -0,0 +1,72 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Bruno JOFRET + * Copyright (C) 2009 - DIGITEO - Vincent COUVERT + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions; + +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +import org.scilab.modules.graph.ScilabGraph; +import org.scilab.modules.graph.actions.base.GraphActionManager; +import org.scilab.modules.graph.actions.base.VertexSelectionDependantAction; +import org.scilab.modules.graph.utils.ScilabGraphMessages; +import org.scilab.modules.gui.menuitem.MenuItem; + +import com.mxgraph.swing.handler.mxGraphTransferHandler; + +/** + * Copy manager + */ +@SuppressWarnings(value = { "serial" }) +public final class CopyAction extends VertexSelectionDependantAction { + /** Name of the action */ + public static final String NAME = ScilabGraphMessages.COPY; + /** Icon name of the action */ + public static final String SMALL_ICON = "edit-copy"; + /** Mnemonic key of the action */ + public static final int MNEMONIC_KEY = KeyEvent.VK_C; + /** Accelerator key for the action */ + public static final int ACCELERATOR_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + + /** + * Constructor + * @param scilabGraph corresponding Scilab Graph + */ + public CopyAction(ScilabGraph scilabGraph) { + super(scilabGraph); + } + + /** + * Create a menu for a graph menubar + * @param scilabGraph corresponding Scilab Graph + * @return the menu + */ + public static MenuItem copyMenu(ScilabGraph scilabGraph) { + return createMenu(scilabGraph, CopyAction.class); + } + + /** + * Action associated + * @param e the event + * @see org.scilab.modules.gui.events.callback.CallBack#actionPerformed(java.awt.event.ActionEvent) + */ + @Override + public void actionPerformed(ActionEvent e) { + mxGraphTransferHandler.getCopyAction().actionPerformed(new ActionEvent(getGraph(e).getAsComponent(), e.getID(), e.getActionCommand())); + + // Enable the paste action + GraphActionManager.setEnable(PasteAction.class, true); + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/CutAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/CutAction.java new file mode 100755 index 000000000..6a699fc3c --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/CutAction.java @@ -0,0 +1,72 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Bruno JOFRET + * Copyright (C) 2009 - DIGITEO - Vincent COUVERT + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions; + +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +import org.scilab.modules.graph.ScilabGraph; +import org.scilab.modules.graph.actions.base.GraphActionManager; +import org.scilab.modules.graph.actions.base.VertexSelectionDependantAction; +import org.scilab.modules.graph.utils.ScilabGraphMessages; +import org.scilab.modules.gui.menuitem.MenuItem; + +import com.mxgraph.swing.handler.mxGraphTransferHandler; + +/** + * Cut manager + */ +@SuppressWarnings(value = { "serial" }) +public final class CutAction extends VertexSelectionDependantAction { + /** Name of the action */ + public static final String NAME = ScilabGraphMessages.CUT; + /** Icon name of the action */ + public static final String SMALL_ICON = "edit-cut"; + /** Mnemonic key of the action */ + public static final int MNEMONIC_KEY = KeyEvent.VK_X; + /** Accelerator key for the action */ + public static final int ACCELERATOR_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + + /** + * Constructor + * @param scilabGraph corresponding Scilab Graph + */ + public CutAction(ScilabGraph scilabGraph) { + super(scilabGraph); + } + + /** + * Create a menu for a graph menubar + * @param scilabGraph corresponding Scilab Graph + * @return the menu + */ + public static MenuItem cutMenu(ScilabGraph scilabGraph) { + return createMenu(scilabGraph, CutAction.class); + } + + /** + * Action associated + * @param e the event + * @see org.scilab.modules.gui.events.callback.CallBack#actionPerformed(java.awt.event.ActionEvent) + */ + @Override + public void actionPerformed(ActionEvent e) { + mxGraphTransferHandler.getCutAction().actionPerformed(new ActionEvent(getGraph(e).getAsComponent(), e.getID(), e.getActionCommand())); + + // Enable the paste action + GraphActionManager.setEnable(PasteAction.class, true); + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/DeleteAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/DeleteAction.java new file mode 100755 index 000000000..60b6952b5 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/DeleteAction.java @@ -0,0 +1,85 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Vincent COUVERT + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions; + +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +import javax.swing.JButton; + +import org.scilab.modules.graph.ScilabComponent; +import org.scilab.modules.graph.ScilabGraph; +import org.scilab.modules.graph.actions.base.OneSelectionDependantAction; +import org.scilab.modules.graph.utils.ScilabGraphMessages; +import org.scilab.modules.gui.menuitem.MenuItem; + +/** + * Delete manager + */ +@SuppressWarnings(value = { "serial" }) +public final class DeleteAction extends OneSelectionDependantAction { + /** Name of the action */ + public static final String NAME = ScilabGraphMessages.DELETE; + /** Icon name of the action */ + public static final String SMALL_ICON = "edit-delete"; + /** Mnemonic key of the action */ + public static final int MNEMONIC_KEY = KeyEvent.VK_DELETE; + /** Accelerator key for the action */ + public static final int ACCELERATOR_KEY = 0; + + /** + * Constructor + * @param scilabGraph corresponding Scilab Graph + */ + public DeleteAction(ScilabGraph scilabGraph) { + super(scilabGraph); + } + + /** + * Create a menu for a graph menubar + * @param scilabGraph corresponding Scilab Graph + * @return the menu + */ + public static MenuItem createMenu(ScilabGraph scilabGraph) { + return createMenu(scilabGraph, DeleteAction.class); + } + + /** + * Create a button for a graph toolbar + * @param scilabGraph corresponding Scilab Graph + * @return the button + */ + public static JButton createButton(ScilabGraph scilabGraph) { + return createButton(scilabGraph, DeleteAction.class); + } + + /** + * Action associated + * @param e the event + * @see org.scilab.modules.gui.events.callback.CallBack#actionPerformed(java.awt.event.ActionEvent) + */ + @Override + public void actionPerformed(ActionEvent e) { + final ScilabGraph graph = getGraph(e); + + // action disabled when the cell is edited + final ScilabComponent comp = ((ScilabComponent) graph.getAsComponent()); + if (comp.isEditing()) { + return; + } + + graph.removeCells(graph.getSelectionCells()); + } + +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/GroupAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/GroupAction.java new file mode 100755 index 000000000..ede74c9aa --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/GroupAction.java @@ -0,0 +1,70 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Bruno JOFRET + * Copyright (C) 2009 - DIGITEO - Vincent COUVERT + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions; + +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +import org.scilab.modules.graph.ScilabGraph; +import org.scilab.modules.graph.actions.base.MultiSelectionDependantAction; +import org.scilab.modules.graph.utils.ScilabGraphMessages; +import org.scilab.modules.gui.menuitem.MenuItem; + +import com.mxgraph.swing.util.mxGraphActions; + +/** + * Group any blocks and ease the manipulation of them. + */ +@SuppressWarnings(value = { "serial" }) +public class GroupAction extends MultiSelectionDependantAction { + /** Name of the action */ + public static final String NAME = ScilabGraphMessages.GROUP; + /** Icon name of the action */ + public static final String SMALL_ICON = ""; + /** Mnemonic key of the action */ + public static final int MNEMONIC_KEY = KeyEvent.VK_G; + /** Accelerator key for the action */ + public static final int ACCELERATOR_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + + /** + * Default constructor + * + * @param scilabGraph + * The associated graph + */ + public GroupAction(ScilabGraph scilabGraph) { + super(scilabGraph); + } + + /** + * Create the menu associated with this action. + * @param scilabGraph the associated graph + * @return The associated menu + */ + public static MenuItem groupMenu(ScilabGraph scilabGraph) { + return createMenu(scilabGraph, GroupAction.class); + } + + /** + * Action to be done + * @param e Event descriptor + */ + public void actionPerformed(ActionEvent e) { + mxGraphActions.getGroupAction().actionPerformed(new ActionEvent(getGraph(e).getAsComponent(), + e.getID(), e.getActionCommand())); + } + +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/InvertSelectionAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/InvertSelectionAction.java new file mode 100755 index 000000000..a4f56b923 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/InvertSelectionAction.java @@ -0,0 +1,73 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Vincent COUVERT + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions; + +import java.awt.event.ActionEvent; + +import org.scilab.modules.graph.ScilabComponent; +import org.scilab.modules.graph.ScilabGraph; +import org.scilab.modules.graph.actions.base.VertexSelectionDependantAction; +import org.scilab.modules.graph.utils.ScilabGraphMessages; +import org.scilab.modules.gui.menuitem.MenuItem; + +/** + * Selection management + */ +@SuppressWarnings(value = { "serial" }) +public final class InvertSelectionAction extends VertexSelectionDependantAction { + /** Name of the action */ + public static final String NAME = ScilabGraphMessages.INVERT_SELECTION; + /** Icon name of the action */ + public static final String SMALL_ICON = ""; + /** Mnemonic key of the action */ + public static final int MNEMONIC_KEY = 0; + /** Accelerator key for the action */ + public static final int ACCELERATOR_KEY = 0; + + /** + * Constructor + * @param scilabGraph corresponding Scilab Graph + */ + public InvertSelectionAction(ScilabGraph scilabGraph) { + super(scilabGraph); + } + + /** + * Create a menu for a graph menubar + * @param scilabGraph corresponding Scilab Graph + * @return the menu + */ + public static MenuItem createMenu(ScilabGraph scilabGraph) { + return createMenu(scilabGraph, InvertSelectionAction.class); + } + + /** + * Action associated + * @param e the event + * @see org.scilab.modules.gui.events.callback.CallBack#actionPerformed(java.awt.event.ActionEvent) + */ + public void actionPerformed(ActionEvent e) { + final ScilabGraph graph = getGraph(e); + + // action disabled when the cell is edited + final ScilabComponent comp = ((ScilabComponent) graph.getAsComponent()); + if (comp.isEditing()) { + return; + } + + Object[] all = graph.getSelectionCells(); + graph.selectAll(); + graph.removeSelectionCells(all); + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/PasteAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/PasteAction.java new file mode 100755 index 000000000..96620494c --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/PasteAction.java @@ -0,0 +1,75 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Bruno JOFRET + * Copyright (C) 2009 - DIGITEO - Vincent COUVERT + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions; + +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +import javax.swing.TransferHandler; + +import org.scilab.modules.graph.ScilabGraph; +import org.scilab.modules.graph.actions.base.DefaultAction; +import org.scilab.modules.graph.utils.ScilabGraphMessages; +import org.scilab.modules.gui.menuitem.MenuItem; + +import com.mxgraph.swing.handler.mxGraphTransferHandler; + +/** + * Paste manager + * + * This action is enabled by the {@link CutAction} and {@link CopyAction}. + */ +@SuppressWarnings(value = { "serial" }) +public final class PasteAction extends DefaultAction { + /** Name of the action */ + public static final String NAME = ScilabGraphMessages.PASTE; + /** Icon name of the action */ + public static final String SMALL_ICON = "edit-paste"; + /** Mnemonic key of the action */ + public static final int MNEMONIC_KEY = KeyEvent.VK_V; + /** Accelerator key for the action */ + public static final int ACCELERATOR_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + + /** + * Constructor + * @param scilabGraph corresponding Scilab Graph + */ + public PasteAction(ScilabGraph scilabGraph) { + super(scilabGraph); + + setEnabled(TransferHandler.getPasteAction().isEnabled()); + } + + /** + * Create a menu for a graph menubar + * @param scilabGraph corresponding Scilab Graph + * @return the menu + */ + public static MenuItem pasteMenu(ScilabGraph scilabGraph) { + return createMenu(scilabGraph, PasteAction.class); + } + + /** + * Action associated + * @param e the event + * @see org.scilab.modules.gui.events.callback.CallBack#actionPerformed(java.awt.event.ActionEvent) + */ + @Override + public void actionPerformed(ActionEvent e) { + mxGraphTransferHandler.getPasteAction().actionPerformed(new ActionEvent(getGraph(e).getAsComponent(), + e.getID(), e.getActionCommand())); + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/RedoAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/RedoAction.java new file mode 100755 index 000000000..f85ce6da2 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/RedoAction.java @@ -0,0 +1,153 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Bruno JOFRET + * Copyright (C) 2009 - DIGITEO - Vincent COUVERT + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions; + +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.lang.ref.WeakReference; + +import javax.swing.JButton; + +import org.scilab.modules.graph.ScilabGraph; +import org.scilab.modules.graph.actions.base.ActionConstraint; +import org.scilab.modules.graph.actions.base.DefaultAction; +import org.scilab.modules.graph.utils.ScilabGraphMessages; +import org.scilab.modules.gui.menuitem.MenuItem; + +import com.mxgraph.util.mxEvent; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.util.mxUndoManager; + +/** + * Redo manager + */ +@SuppressWarnings(value = { "serial" }) +public class RedoAction extends DefaultAction { + /** Name of the action */ + public static final String NAME = ScilabGraphMessages.REDO; + /** Icon name of the action */ + public static final String SMALL_ICON = "edit-redo"; + /** Mnemonic key of the action */ + public static final int MNEMONIC_KEY = KeyEvent.VK_Y; + /** Accelerator key for the action */ + public static final int ACCELERATOR_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + + /** + * Manage enable modification + */ + private final class RedoConstraint extends ActionConstraint { + private final WeakReference<ScilabGraph> scilabGraph; + + /** + * Default constructor + * @param scilabGraph the graph to work on + */ + public RedoConstraint(ScilabGraph scilabGraph) { + this.scilabGraph = new WeakReference<ScilabGraph>(scilabGraph); + } + + /** + * @param action + * the current action + * @param scilabGraph + * the associated graph + * @see org.scilab.modules.graph.actions.base.ActionConstraint#install(org.scilab.modules.graph.actions.base.DefaultAction, + * org.scilab.modules.graph.ScilabGraph) + */ + @Override + public void install(DefaultAction action, ScilabGraph scilabGraph) { + super.install(action, scilabGraph); + + registerAsListener(scilabGraph.getUndoManager()); + } + + /** + * @param manager the associated UndoManager + */ + public void registerAsListener(mxUndoManager manager) { + manager.addListener(mxEvent.UNDO, this); + manager.addListener(mxEvent.REDO, this); + manager.addListener(mxEvent.ADD, this); + manager.addListener(mxEvent.CLEAR, this); + } + + /** + * To be checked + * @param sender the event sender + * @param evt the current event + * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object, com.mxgraph.util.mxEventObject) + */ + @Override + public void invoke(Object sender, mxEventObject evt) { + final ScilabGraph graph = scilabGraph.get(); + if (graph == null) { + return; + } + + boolean canRedo = graph.getUndoManager().canRedo(); + super.setEnabled(canRedo); + } + } + + /** + * Constructor + * @param scilabGraph corresponding Scilab Graph + */ + public RedoAction(ScilabGraph scilabGraph) { + super(scilabGraph); + + RedoConstraint c = new RedoConstraint(scilabGraph); + c.install(this, scilabGraph); + } + + /** + * Create a button for a graph toolbar + * @param scilabGraph corresponding Scilab Graph + * @return the button + */ + public static JButton redoButton(ScilabGraph scilabGraph) { + return createButton(scilabGraph, RedoAction.class); + } + + /** + * Create a menu for a graph menubar + * @param scilabGraph corresponding Scilab Graph + * @return the menu + */ + public static MenuItem redoMenu(ScilabGraph scilabGraph) { + return createMenu(scilabGraph, RedoAction.class); + } + + /** + * Action associated + * + * @param e + * the event + * @see org.scilab.modules.gui.events.callback.CallBack#actionPerformed(java.awt.event.ActionEvent) + */ + @Override + public void actionPerformed(ActionEvent e) { + final ScilabGraph graph = getGraph(e); + + graph.removeUndoHandler(); + graph.getUndoManager().redo(); + graph.registerUndoHandler(); + + // revalidate the graph + graph.getAsComponent().clearCellOverlays(); + graph.getAsComponent().validateGraph(); + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/SelectAllAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/SelectAllAction.java new file mode 100755 index 000000000..3f172f639 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/SelectAllAction.java @@ -0,0 +1,65 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Vincent COUVERT + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions; + +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +import org.scilab.modules.graph.ScilabGraph; +import org.scilab.modules.graph.actions.base.OneBlockDependantAction; +import org.scilab.modules.graph.utils.ScilabGraphMessages; +import org.scilab.modules.gui.menuitem.MenuItem; + +/** + * Selection management + */ +@SuppressWarnings(value = { "serial" }) +public final class SelectAllAction extends OneBlockDependantAction { + /** Name of the action */ + public static final String NAME = ScilabGraphMessages.SELECT_ALL; + /** Icon name of the action */ + public static final String SMALL_ICON = "edit-select-all"; + /** Mnemonic key of the action */ + public static final int MNEMONIC_KEY = KeyEvent.VK_A; + /** Accelerator key for the action */ + public static final int ACCELERATOR_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + + /** + * Constructor + * @param scilabGraph corresponding Scilab Graph + */ + public SelectAllAction(ScilabGraph scilabGraph) { + super(scilabGraph); + } + + /** + * Create a menu for a graph menubar + * @param scilabGraph corresponding Scilab Graph + * @return the menu + */ + public static MenuItem createMenu(ScilabGraph scilabGraph) { + return createMenu(scilabGraph, SelectAllAction.class); + } + + /** + * Action associated + * @param e the event + * @see org.scilab.modules.gui.events.callback.CallBack#actionPerformed(java.awt.event.ActionEvent) + */ + @Override + public void actionPerformed(ActionEvent e) { + getGraph(e).selectAll(); + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/UnGroupAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/UnGroupAction.java new file mode 100755 index 000000000..a45068520 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/UnGroupAction.java @@ -0,0 +1,71 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Bruno JOFRET + * Copyright (C) 2009 - DIGITEO - Vincent COUVERT + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions; + +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +import org.scilab.modules.graph.ScilabGraph; +import org.scilab.modules.graph.actions.base.DefaultAction; +import org.scilab.modules.graph.utils.ScilabGraphMessages; +import org.scilab.modules.gui.menuitem.MenuItem; + +import com.mxgraph.swing.util.mxGraphActions; + +/** + * Ungroup any blocks and ease the manipulation of them. + */ +@SuppressWarnings(value = { "serial" }) +public class UnGroupAction extends DefaultAction { + /** Name of the action */ + public static final String NAME = ScilabGraphMessages.UNGROUP; + /** Icon name of the action */ + public static final String SMALL_ICON = ""; + /** Mnemonic key of the action */ + public static final int MNEMONIC_KEY = KeyEvent.VK_G; + /** Accelerator key for the action */ + public static final int ACCELERATOR_KEY = KeyEvent.SHIFT_DOWN_MASK + | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + + /** + * Default constructor + * + * @param scilabGraph + * The associated graph + */ + public UnGroupAction(ScilabGraph scilabGraph) { + super(scilabGraph); + } + + /** + * Create the menu associated with this action. + * @param scilabGraph the associated graph + * @return The associated menu + */ + public static MenuItem ungroupMenu(ScilabGraph scilabGraph) { + return createMenu(scilabGraph, UnGroupAction.class); + } + + /** + * Action to be done + * @param e Event descriptor + */ + public void actionPerformed(ActionEvent e) { + mxGraphActions.getUngroupAction().actionPerformed(new ActionEvent(getGraph(e).getAsComponent(), + e.getID(), e.getActionCommand())); + } + +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/UndoAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/UndoAction.java new file mode 100755 index 000000000..487c79974 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/UndoAction.java @@ -0,0 +1,153 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Bruno JOFRET + * Copyright (C) 2009 - DIGITEO - Vincent COUVERT + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions; + +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.lang.ref.WeakReference; + +import javax.swing.JButton; + +import org.scilab.modules.graph.ScilabGraph; +import org.scilab.modules.graph.actions.base.ActionConstraint; +import org.scilab.modules.graph.actions.base.DefaultAction; +import org.scilab.modules.graph.utils.ScilabGraphMessages; +import org.scilab.modules.gui.menuitem.MenuItem; + +import com.mxgraph.util.mxEvent; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.util.mxUndoManager; + +/** + * Undo manager + */ +@SuppressWarnings(value = { "serial" }) +public class UndoAction extends DefaultAction { + /** Name of the action */ + public static final String NAME = ScilabGraphMessages.UNDO; + /** Icon name of the action */ + public static final String SMALL_ICON = "edit-undo"; + /** Mnemonic key of the action */ + public static final int MNEMONIC_KEY = KeyEvent.VK_Z; + /** Accelerator key for the action */ + public static final int ACCELERATOR_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + + /** + * Manage enable modification + */ + private final class UndoConstraint extends ActionConstraint { + private final WeakReference<ScilabGraph> scilabGraph; + + /** + * Default constructor + * @param scilabGraph the associated scilab graph + */ + public UndoConstraint(ScilabGraph scilabGraph) { + this.scilabGraph = new WeakReference<ScilabGraph>(scilabGraph); + } + + /** + * @param action + * the current action + * @param scilabGraph + * the associated graph + * @see org.scilab.modules.graph.actions.base.ActionConstraint#install(org.scilab.modules.graph.actions.base.DefaultAction, + * org.scilab.modules.graph.ScilabGraph) + */ + @Override + public void install(DefaultAction action, ScilabGraph scilabGraph) { + super.install(action, scilabGraph); + registerAsListener(scilabGraph.getUndoManager()); + } + + /** + * @param manager the associated UndoManager + */ + private void registerAsListener(mxUndoManager manager) { + manager.addListener(mxEvent.UNDO, this); + manager.addListener(mxEvent.REDO, this); + manager.addListener(mxEvent.ADD, this); + manager.addListener(mxEvent.CLEAR, this); + } + + /** + * To be checked + * @param sender the event sender + * @param evt the current event + * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object, com.mxgraph.util.mxEventObject) + */ + @Override + public void invoke(Object sender, mxEventObject evt) { + final ScilabGraph graph = scilabGraph.get(); + if (graph == null) { + return; + } + + boolean canUndo = graph.getUndoManager().canUndo(); + super.setEnabled(canUndo); + } + } + + /** + * Constructor + * @param scilabGraph corresponding Scilab Graph + */ + public UndoAction(ScilabGraph scilabGraph) { + super(scilabGraph); + + UndoConstraint c = new UndoConstraint(scilabGraph); + c.install(this, scilabGraph); + } + + /** + * Create a button for a graph toolbar + * @param scilabGraph corresponding Scilab Graph + * @return the button + */ + public static JButton undoButton(ScilabGraph scilabGraph) { + return createButton(scilabGraph, UndoAction.class); + } + + /** + * Create a menu for a graph menubar + * @param scilabGraph corresponding Scilab Graph + * @return the menu + */ + public static MenuItem undoMenu(ScilabGraph scilabGraph) { + return createMenu(scilabGraph, UndoAction.class); + } + + /** + * Action associated + * + * @param e + * the event + * @see org.scilab.modules.gui.events.callback.CallBack#actionPerformed(java.awt.event.ActionEvent) + */ + @Override + public void actionPerformed(ActionEvent e) { + final ScilabGraph graph = getGraph(e); + + graph.removeUndoHandler(); + graph.getUndoManager().undo(); + graph.registerUndoHandler(); + + // revalidate the graph + graph.getAsComponent().clearCellOverlays(); + graph.getAsComponent().validateGraph(); + } + +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/ZoomInAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/ZoomInAction.java new file mode 100755 index 000000000..3d92baf8e --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/ZoomInAction.java @@ -0,0 +1,158 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Bruno JOFRET + * Copyright (C) 2009 - DIGITEO - Vincent COUVERT + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions; + +import static org.scilab.modules.commons.OS.MAC; + +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; + +import javax.swing.ActionMap; +import javax.swing.InputMap; +import javax.swing.JButton; +import javax.swing.KeyStroke; + +import org.scilab.modules.commons.OS; +import org.scilab.modules.graph.ScilabGraph; +import org.scilab.modules.graph.actions.base.DefaultAction; +import org.scilab.modules.graph.utils.ScilabGraphMessages; +import org.scilab.modules.gui.menuitem.MenuItem; + +/** + * Zoom management + */ +@SuppressWarnings(value = { "serial" }) +public class ZoomInAction extends DefaultAction { + /** Name of the action */ + public static final String NAME = ScilabGraphMessages.ZOOM_IN; + /** Icon name of the action */ + public static final String SMALL_ICON = "zoom-in"; + /** Mnemonic key of the action */ + public static final int MNEMONIC_KEY = KeyEvent.VK_ADD; + /** Accelerator key for the action */ + public static final int ACCELERATOR_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + + /** key used on {@link InputMap} for this action */ + private static final String ZOOM_IN = "zoomIn"; + + /** + * Implement custom mouse handling for the zoom + */ + private static final class CustomMouseWheelListener implements MouseWheelListener { + private final ScilabGraph scilabGraph; + + /** + * Default constructor + * + * @param scilabGraph + * the current graph + */ + public CustomMouseWheelListener(ScilabGraph scilabGraph) { + this.scilabGraph = scilabGraph; + } + + /** + * When the wheel is used + * + * @param e + * the parameters + * @see java.awt.event.MouseWheelListener#mouseWheelMoved(java.awt.event.MouseWheelEvent) + */ + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + if ((e.getModifiers() & ACCELERATOR_KEY) != 0) { + if (e.getWheelRotation() < 0) { + scilabGraph.getAsComponent().zoomIn(); + } + } + } + } + + /** + * Constructor + * + * @param scilabGraph + * corresponding Scilab Graph + */ + public ZoomInAction(ScilabGraph scilabGraph) { + super(scilabGraph); + + MouseWheelListener mouseListener = new CustomMouseWheelListener(scilabGraph); + scilabGraph.getAsComponent().addMouseWheelListener(mouseListener); + + // Multi-shortcut action + final ActionMap am = scilabGraph.getAsComponent().getActionMap(); + final InputMap map = scilabGraph.getAsComponent().getInputMap(); + + // register the action to a unique action keyword + am.put(ZOOM_IN, this); + + // add custom key stroke for this action + final KeyStroke[] keystrokes; + if (OS.get() == MAC) { + /* + * AZERTY for Mac has a non-supported classic layout + */ + keystrokes = new KeyStroke[] { KeyStroke.getKeyStroke('/', ACCELERATOR_KEY), + KeyStroke.getKeyStroke('/', ACCELERATOR_KEY | KeyEvent.SHIFT_DOWN_MASK), + }; + } else { + keystrokes = new KeyStroke[] { KeyStroke.getKeyStroke('=', ACCELERATOR_KEY), + KeyStroke.getKeyStroke('=', ACCELERATOR_KEY | KeyEvent.SHIFT_DOWN_MASK), KeyStroke.getKeyStroke('+', ACCELERATOR_KEY), + KeyStroke.getKeyStroke('+', ACCELERATOR_KEY | KeyEvent.SHIFT_DOWN_MASK), + }; + } + + for (KeyStroke k : keystrokes) { + map.put(k, ZOOM_IN); + } + } + + /** + * Create a button for a graph toolbar + * + * @param scilabGraph + * corresponding Scilab Graph + * @return the button + */ + public static JButton zoominButton(ScilabGraph scilabGraph) { + return createButton(scilabGraph, ZoomInAction.class); + } + + /** + * Create a menu for a graph menubar + * + * @param scilabGraph + * corresponding Scilab Graph + * @return the menu + */ + public static MenuItem zoominMenu(ScilabGraph scilabGraph) { + return createMenu(scilabGraph, ZoomInAction.class); + } + + /** + * Action associated + * + * @param e + * the event + * @see org.scilab.modules.gui.events.callback.CallBack#actionPerformed(java.awt.event.ActionEvent) + */ + @Override + public void actionPerformed(ActionEvent e) { + getGraph(e).getAsComponent().zoomIn(); + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/ZoomOutAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/ZoomOutAction.java new file mode 100755 index 000000000..6625874b8 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/ZoomOutAction.java @@ -0,0 +1,162 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Bruno JOFRET + * Copyright (C) 2009 - DIGITEO - Vincent COUVERT + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions; + +import static org.scilab.modules.commons.OS.MAC; + +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; + +import javax.swing.ActionMap; +import javax.swing.InputMap; +import javax.swing.JButton; +import javax.swing.KeyStroke; + +import org.scilab.modules.commons.OS; +import org.scilab.modules.graph.ScilabGraph; +import org.scilab.modules.graph.actions.base.DefaultAction; +import org.scilab.modules.graph.utils.ScilabGraphMessages; +import org.scilab.modules.gui.menuitem.MenuItem; + +/** + * Zoom management + * + * @author Bruno JOFFRET + */ +@SuppressWarnings(value = { "serial" }) +public class ZoomOutAction extends DefaultAction implements ActionListener { + /** Name of the action */ + public static final String NAME = ScilabGraphMessages.ZOOM_OUT; + /** Icon name of the action */ + public static final String SMALL_ICON = "zoom-out"; + /** Mnemonic key of the action */ + public static final int MNEMONIC_KEY = KeyEvent.VK_SUBTRACT; + /** Accelerator key for the action */ + public static final int ACCELERATOR_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); + + /** key used on {@link InputMap} for this action */ + private static final String ZOOM_OUT = "zoomOut"; + + /** + * Implement custom mouse handling for the zoom + */ + private static final class CustomMouseWheelListener implements MouseWheelListener { + private final ScilabGraph scilabGraph; + + /** + * Default constructor + * + * @param scilabGraph + * the current graph + */ + public CustomMouseWheelListener(ScilabGraph scilabGraph) { + this.scilabGraph = scilabGraph; + } + + /** + * When the wheel is used + * + * @param e + * the parameters + * @see java.awt.event.MouseWheelListener#mouseWheelMoved(java.awt.event.MouseWheelEvent) + */ + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + if ((e.getModifiers() & ACCELERATOR_KEY) != 0) { + if (e.getWheelRotation() > 0) { + scilabGraph.getAsComponent().zoomOut(); + } + } + } + } + + /** + * Constructor + * + * @param scilabGraph + * corresponding Scilab Graph + */ + public ZoomOutAction(ScilabGraph scilabGraph) { + super(scilabGraph); + + MouseWheelListener mouseListener = new CustomMouseWheelListener(scilabGraph); + scilabGraph.getAsComponent().addMouseWheelListener(mouseListener); + + // Multi-shortcut action + final ActionMap am = scilabGraph.getAsComponent().getActionMap(); + final InputMap map = scilabGraph.getAsComponent().getInputMap(); + + // register the action to a unique action keyword + am.put(ZOOM_OUT, this); + + // add custom key stroke for this action + final KeyStroke[] keystrokes; + if (OS.get() == MAC) { + /* + * AZERTY for Mac has a non-supported classic layout + */ + keystrokes = new KeyStroke[] { KeyStroke.getKeyStroke('=', ACCELERATOR_KEY), + KeyStroke.getKeyStroke('=', ACCELERATOR_KEY | KeyEvent.SHIFT_DOWN_MASK), + }; + } else { + keystrokes = new KeyStroke[] { KeyStroke.getKeyStroke('-', ACCELERATOR_KEY), + KeyStroke.getKeyStroke('-', ACCELERATOR_KEY | KeyEvent.SHIFT_DOWN_MASK), KeyStroke.getKeyStroke('_', ACCELERATOR_KEY), + KeyStroke.getKeyStroke('_', ACCELERATOR_KEY | KeyEvent.SHIFT_DOWN_MASK), + }; + } + + for (KeyStroke k : keystrokes) { + map.put(k, ZOOM_OUT); + } + } + + /** + * Create a button for a graph toolbar + * + * @param scilabGraph + * corresponding Scilab Graph + * @return the button + */ + public static JButton zoomoutButton(ScilabGraph scilabGraph) { + return createButton(scilabGraph, ZoomOutAction.class); + } + + /** + * Create a menu for a graph menubar + * + * @param scilabGraph + * corresponding Scilab Graph + * @return the menu + */ + public static MenuItem zoomoutMenu(ScilabGraph scilabGraph) { + return createMenu(scilabGraph, ZoomOutAction.class); + } + + /** + * Action associated + * + * @param e + * the event + * @see org.scilab.modules.gui.events.callback.CallBack#actionPerformed(java.awt.event.ActionEvent) + */ + @Override + public void actionPerformed(ActionEvent e) { + getGraph(e).getAsComponent().zoomOut(); + } + +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/base/ActionConstraint.java b/modules/graph/src/java/org/scilab/modules/graph/actions/base/ActionConstraint.java new file mode 100755 index 000000000..ed6664b4d --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/base/ActionConstraint.java @@ -0,0 +1,86 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions.base; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import org.scilab.modules.graph.ScilabGraph; + +import com.mxgraph.util.mxEventSource.mxIEventListener; + +/** + * Specify function called to add constraint on an action. + */ +public abstract class ActionConstraint implements mxIEventListener { + private boolean enabled; + private DefaultAction action; + + /** + * Install this constraint on a graph for the specific action. + * + * Override to action to install the current listener on a graph. Call the + * super.install(...) in order to automatically register your action. + * + * @param action + * the constrained action + * @param scilabGraph + * where to install constraint + */ + public void install(final DefaultAction action, ScilabGraph scilabGraph) { + this.action = action; + + /** + * assume that at installation time, the constraint is not valid. + */ + setEnabled(false); + + /* + * Disable actions when cells are locked + */ + scilabGraph.addPropertyChangeListener(new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals("cellsLocked")) { + boolean locked = (Boolean) evt.getNewValue(); + action.setEnabled(!locked); + } + } + }); + + // If the current constraint is not valid : force setEnable(false). + // This will force the current constraint. + action.addPropertyChangeListener(new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + if (!isEnabled()) { + action.setEnabled(false); + } + } + }); + } + + /** + * @return That status of the action according to implementation conditions. + */ + public boolean isEnabled() { + return enabled; + } + + /** + * @param enabled Update the enable status of this constraint. + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + + action.setEnabled(enabled); + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/base/DefaultAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/base/DefaultAction.java new file mode 100755 index 000000000..db5c2eaf2 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/base/DefaultAction.java @@ -0,0 +1,271 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2009 - DIGITEO - Bruno JOFRET + * Copyright (C) 2009-2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions.base; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.lang.ref.WeakReference; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.KeyStroke; + +import org.scilab.modules.commons.gui.FindIconHelper; +import org.scilab.modules.commons.gui.ScilabLAF; +import org.scilab.modules.graph.ScilabGraph; +import org.scilab.modules.gui.bridge.checkboxmenuitem.SwingScilabCheckBoxMenuItem; +import org.scilab.modules.gui.bridge.menuitem.SwingScilabMenuItem; +import org.scilab.modules.gui.checkboxmenuitem.CheckBoxMenuItem; +import org.scilab.modules.gui.checkboxmenuitem.ScilabCheckBoxMenuItem; +import org.scilab.modules.gui.events.callback.CommonCallBack; +import org.scilab.modules.gui.menuitem.MenuItem; +import org.scilab.modules.gui.menuitem.ScilabMenuItem; + +import com.mxgraph.swing.mxGraphComponent; + +/** + * Default action for a Scilab Graph + */ +public abstract class DefaultAction extends CommonCallBack { + private final WeakReference<ScilabGraph> scilabGraph; + + /** + * Default constructor. + * + * The {@link AbstractAction} object is configured using the reflection API. + * So you have to be sure that the following fields are declared as static + * final fields of each subclasses. + * <ul> + * <li>String NAME : The name of the action</li> + * <li>String SMALL_ICON : The associated icon name (located on + * $SCI/modules/gui/images/icons)</li> + * <li>int MNEMONIC_KEY : The key associated with the action (see + * {@link KeyEvent})</li> + * <li>int ACCELERATOR_KEY : The key mask to apply to the mnemonic</li> + * </ul> + * + * @param scilabGraph + * corresponding Scilab Graph + */ + public DefaultAction(ScilabGraph scilabGraph) { + super(""); + this.scilabGraph = new WeakReference<ScilabGraph>(scilabGraph); + + installProperties(); + } + + /** + * Install the static actions properties on the instance + */ + private void installProperties() { + String name = ""; + ImageIcon icon = null; + int mnemonic = 0; + int accelerator = 0; + try { + name = (String) getClass().getField("NAME").get(null); + + /* + * Getting icon from the registered icon path + */ + String iconName = (String) getClass().getField("SMALL_ICON").get(null); + if (iconName != null && !iconName.isEmpty()) { + icon = new ImageIcon(FindIconHelper.findIcon(iconName)); + } + + mnemonic = getClass().getField("MNEMONIC_KEY").getInt(null); + accelerator = getClass().getField("ACCELERATOR_KEY").getInt(null); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } + + assert !"".equals(name); + putValue(Action.NAME, name); + putValue(Action.SHORT_DESCRIPTION, name); + putValue(Action.LONG_DESCRIPTION, name); + if (icon != null) { + putValue(Action.SMALL_ICON, icon); + } + + /* + * Set up the accelerator instead of the mnemonic as the menu is the + * preferred way on keyboard control. We are using Action.MNEMONIC_KEY + * as keyboard key and Action.ACCELERATOR_KEY as a mask. + * + * Install it only when there is a real shortcut (with a mnemonic). + */ + if (mnemonic != 0) { + putValue(Action.MNEMONIC_KEY, mnemonic); + putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(mnemonic, accelerator)); + } + } + + /** + * Create a menu item associated with the graph + * + * @param graph + * the graph to work on + * @param klass + * the associated klass + * @return the menu item + */ + protected static MenuItem createMenu(ScilabGraph graph, final Class <? extends DefaultAction > klass) { + DefaultAction action = GraphActionManager.getInstance(graph, klass); + MenuItem item = ScilabMenuItem.createMenuItem(); + + SwingScilabMenuItem swingItem = (SwingScilabMenuItem) item.getAsSimpleMenuItem(); + swingItem.setAction(action); + + return item; + } + + /** + * Create a menu item associated with the graph + * + * @param graph + * the graph to work on + * @param klass + * the associated klass + * @return the push button + */ + protected static JButton createButton(ScilabGraph graph, final Class <? extends DefaultAction > klass) { + DefaultAction action = GraphActionManager.getInstance(graph, klass); + JButton item = new JButton(); + + item.setAction(action); + + // Hide the text on buttons + item.setHideActionText(true); + + return item; + } + + /** + * Create a menu item associated with the graph + * + * @param graph + * the graph to work on + * @param klass + * the associated klass + * @return the checkbox item + */ + protected static CheckBoxMenuItem createCheckBoxMenu(ScilabGraph graph, Class <? extends DefaultAction > klass) { + DefaultAction action = GraphActionManager.getInstance(graph, klass); + CheckBoxMenuItem item = ScilabCheckBoxMenuItem.createCheckBoxMenuItem(); + + SwingScilabCheckBoxMenuItem swingItem = (SwingScilabCheckBoxMenuItem) item.getAsSimpleCheckBoxMenuItem(); + swingItem.setAction(action); + + return item; + } + + /** + * Constructor + * + * @param label + * action descriptor + * @param scilabGraph + * associated Scilab Graph + */ + @Deprecated + protected DefaultAction(String label, ScilabGraph scilabGraph) { + super(label); + this.scilabGraph = new WeakReference<ScilabGraph>(scilabGraph); + } + + /** + * Get associated graph + * + * @param e + * event + * @return Returns the graph for the given action event. + */ + protected final ScilabGraph getGraph(ActionEvent e) { + final ScilabGraph graph = scilabGraph.get(); + if (graph != null) { + return graph; + } + + if (e == null) { + return null; + } + + if (e.getSource() instanceof Component) { + Component component = (Component) e.getSource(); + + while (component != null && !(component instanceof mxGraphComponent)) { + component = component.getParent(); + } + + return (ScilabGraph) ((mxGraphComponent) component).getGraph(); + } + + return null; + } + + /** + * Create a button for a graph toolbar + * + * @param title + * label of the menu + * @param icon + * the path the an icon file + * @param listener + * action listener associated + * @param keyStroke + * menu shortcut + * @return the button + */ + @Deprecated + protected static MenuItem createMenu(String title, String icon, DefaultAction listener, KeyStroke keyStroke) { + MenuItem menu = ScilabMenuItem.createMenuItem(); + menu.setCallback(listener); + menu.setText(title); + + if (keyStroke != null) { + ((SwingScilabMenuItem) menu.getAsSimpleMenuItem()).setAccelerator(keyStroke); + } + + return menu; + } + + /** + * Action + * + * @param e + * parameters + * @see org.scilab.modules.gui.events.callback.CallBack#actionPerformed(java.awt.event.ActionEvent) + */ + @Override + public abstract void actionPerformed(ActionEvent e); + + /** + * Not used + * + * @see org.scilab.modules.gui.events.callback.CallBack#callBack() + */ + @Override + public void callBack() { + assert "Must never be called as we bypass Callback.java".equals(""); + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/base/EdgeSelectionDependantAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/base/EdgeSelectionDependantAction.java new file mode 100755 index 000000000..bd32df7db --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/base/EdgeSelectionDependantAction.java @@ -0,0 +1,97 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions.base; + +import org.scilab.modules.graph.ScilabGraph; + +import com.mxgraph.model.mxCell; +import com.mxgraph.util.mxEvent; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.view.mxGraphSelectionModel; + +/** + * Common class for selection dependent actions. + * + * Children of this class will be activated when there something selected. If + * not, the action will be disabled. + */ +public abstract class EdgeSelectionDependantAction extends DefaultAction { + + /** + * Enable the selection if there is at least a vertex in the selection. + */ + private final class EdgeSelectionDependantConstraint extends ActionConstraint { + + /** + * Default constructor + */ + public EdgeSelectionDependantConstraint() { + super(); + } + + /** + * @param action the action + * @param scilabGraph the current graph + * @see org.scilab.modules.graph.actions.base.ActionConstraint#install(org.scilab.modules.graph.actions.base.DefaultAction, + * org.scilab.modules.graph.ScilabGraph) + */ + @Override + public void install(DefaultAction action, ScilabGraph scilabGraph) { + super.install(action, scilabGraph); + + scilabGraph.getSelectionModel().addListener(mxEvent.UNDO, this); + } + + /** + * @param sender the sender + * @param evt the event + * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object, com.mxgraph.util.mxEventObject) + */ + public void invoke(Object sender, mxEventObject evt) { + mxGraphSelectionModel selection = (mxGraphSelectionModel) sender; + Object[] cells = selection.getCells(); + + boolean edgeFound = false; + if (cells != null) { + for (Object object : cells) { + if (object instanceof mxCell) { + mxCell cell = (mxCell) object; + edgeFound = cell.isEdge(); + } + + if (edgeFound) { + break; + } + } + + setEnabled(edgeFound); + } + } + + } + + /** + * Default constructor + * + * @param scilabGraph + * The associated graph + */ + public EdgeSelectionDependantAction(ScilabGraph scilabGraph) { + super(scilabGraph); + + if (scilabGraph != null) { + EdgeSelectionDependantConstraint c = new EdgeSelectionDependantConstraint(); + c.install(this, scilabGraph); + } + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/base/GraphActionManager.java b/modules/graph/src/java/org/scilab/modules/graph/actions/base/GraphActionManager.java new file mode 100755 index 000000000..a4b3c123e --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/base/GraphActionManager.java @@ -0,0 +1,220 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions.base; + +import java.lang.reflect.InvocationTargetException; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.WeakHashMap; + +import javax.swing.Action; +import javax.swing.KeyStroke; + +import org.scilab.modules.graph.ScilabGraph; + +/** + * Implement construction methods for Actions. + */ +public final class GraphActionManager { + private static Map<ScilabGraph, Set<DefaultAction>> perGraphAction = new WeakHashMap<ScilabGraph, Set<DefaultAction>>(); + private static Set<DefaultAction> nullGraphAction = Collections.newSetFromMap(new WeakHashMap<DefaultAction, Boolean>()); + + /** + * Static class so private constructor + */ + private GraphActionManager() { + } + + /** + * As each action is unique per graph, get the action instance for the + * graph. + * + * If the action has never been instantiated, use the + * DefaultAction(ScilabGraph) constructor and put it on the set. + * + * @param graph + * the graph to work on + * @param action + * the action class we are looking for. + * @param <T> + * the type to work with. + * @return the instance or null on error. + */ + public static <T extends DefaultAction> T getInstance(ScilabGraph graph, Class<T> action) { + Set<DefaultAction> actionSet = getActionSet(graph); + + for (DefaultAction defaultAction : actionSet) { + if (defaultAction.getClass() == action) { + // Here we can safely cast the action according to the test. + return (T) defaultAction; + } + } + + T instance = null; + try { + instance = action.getConstructor(ScilabGraph.class).newInstance(graph); + actionSet.add(instance); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + + return instance; + } + + /** + * Get the action set attached to the graph + * + * @param graph + * the graph where to search + * @return the {@link DefaultAction} set + */ + private static Set<DefaultAction> getActionSet(ScilabGraph graph) { + Set<DefaultAction> actionSet; + if (graph == null) { + actionSet = nullGraphAction; + } else { + if (perGraphAction.containsKey(graph)) { + actionSet = perGraphAction.get(graph); + } else { + actionSet = Collections.newSetFromMap(new WeakHashMap<DefaultAction, Boolean>()); + perGraphAction.put(graph, actionSet); + } + } + return actionSet; + } + + /** + * As each action is unique per graph, get the action instance for the + * graph. + * + * If the action has never been instantiated, return null. + * + * @param graph + * the graph to work on + * @param action + * the action class we are looking for. + * @param <T> + * the type to work with. + * @return the instance or null if not found. + */ + public static <T extends DefaultAction> T get(ScilabGraph graph, Class<T> action) { + Set<DefaultAction> actionSet = getActionSet(graph); + + for (DefaultAction defaultAction : actionSet) { + if (defaultAction.getClass() == action) { + // Here we can safely cast the action according to the test. + return (T) defaultAction; + } + } + + return null; + } + + /** + * Enable or disable action on all registered graph. + * + * @param actionKlass + * The action type to enable + * @param enable + * the enable status + */ + public static void setEnable(Class <? extends DefaultAction > actionKlass, boolean enable) { + // Handle null graph + for (DefaultAction action : nullGraphAction) { + if (actionKlass.isInstance(action)) { + action.setEnabled(enable); + } + } + + // Handle non-null graph + for (Set<DefaultAction> actions : perGraphAction.values()) { + for (DefaultAction action : actions) { + if (actionKlass.isInstance(action)) { + action.setEnabled(enable); + } + } + } + } + + /** + * Get status action on all registered graph. + * + * @param actionKlass + * The action type to enable + * @return the status (true if all action are enabled, false otherwise) + */ + public static boolean getEnable(Class <? extends DefaultAction > actionKlass) { + boolean result = true; + + // Handle null graph + for (DefaultAction action : nullGraphAction) { + if (actionKlass.isInstance(action)) { + result &= action.isEnabled(); + } + } + + // Handle non-null graph + for (Set<DefaultAction> actions : perGraphAction.values()) { + for (DefaultAction action : actions) { + if (actionKlass.isInstance(action)) { + result &= action.isEnabled(); + } + } + } + + return result; + } + + /** + * Update the {@link KeyStroke} of all actions of class. + * + * @param actionKlass + * the action class to update + * @param key + * the {@link KeyStroke} to set + */ + public static void updateActionKeyStroke(Class <? extends DefaultAction > actionKlass, KeyStroke key) { + /* + * Per graph action + */ + final Set<Entry<ScilabGraph, Set<DefaultAction>>> entrySet = perGraphAction.entrySet(); + for (Entry<ScilabGraph, Set<DefaultAction>> entry : entrySet) { + setKeyStroke(entry.getValue(), actionKlass, key); + } + + /* + * Out of graphs actions + */ + setKeyStroke(nullGraphAction, actionKlass, key); + } + + private static final void setKeyStroke(final Set<DefaultAction> actions, final Class <? extends DefaultAction > actionKlass, final KeyStroke key) { + for (final DefaultAction a : actions) { + if (a.getClass() == actionKlass) { + a.putValue(Action.ACCELERATOR_KEY, key); + } + } + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/base/MinimalNumberOfCellsConstraint.java b/modules/graph/src/java/org/scilab/modules/graph/actions/base/MinimalNumberOfCellsConstraint.java new file mode 100755 index 000000000..b042d9dea --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/base/MinimalNumberOfCellsConstraint.java @@ -0,0 +1,68 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions.base; + +import org.scilab.modules.graph.ScilabGraph; + +import com.mxgraph.model.mxGraphModel; +import com.mxgraph.util.mxEvent; +import com.mxgraph.util.mxEventObject; + +/** + * Constraint which specify how many blocks must be present on a graph to enable + * the action. + */ +public final class MinimalNumberOfCellsConstraint extends ActionConstraint { + private final int minimalCount; + private int numberOfCells; + + /** + * Default constructor + * @param minimalCount the minimal number of cells for the action to be enabled + */ + public MinimalNumberOfCellsConstraint(int minimalCount) { + numberOfCells = 0; + + // add the root and the default parent to the minimal count + this.minimalCount = minimalCount + 2; + } + + /** + * Install this constraint on a graph for the specific action. + * @param action the constrained action + * @param scilabGraph where to install constraint + */ + public void install(final DefaultAction action, ScilabGraph scilabGraph) { + super.install(action, scilabGraph); + scilabGraph.addListener(mxEvent.CELLS_ADDED, this); + scilabGraph.addListener(mxEvent.CELLS_REMOVED, this); + scilabGraph.addListener(mxEvent.ROOT, this); + } + + /** + * Enable or disable the action. + * + * @param sender + * the sender + * @param evt + * the event + * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object, + * com.mxgraph.util.mxEventObject) + */ + public void invoke(Object sender, mxEventObject evt) { + final mxGraphModel model = (mxGraphModel) ((ScilabGraph) sender).getModel(); + numberOfCells = model.getCells().size(); + + setEnabled(numberOfCells >= minimalCount); + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/base/MultiSelectionDependantAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/base/MultiSelectionDependantAction.java new file mode 100755 index 000000000..936618dcc --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/base/MultiSelectionDependantAction.java @@ -0,0 +1,38 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions.base; + +import org.scilab.modules.graph.ScilabGraph; + +/** + * Common class for multi-selection dependent actions. + * + * Children of this class will be activated when there something selected. If + * not, the action will be disabled. + */ +public abstract class MultiSelectionDependantAction extends DefaultAction { + /** + * Default constructor + * + * @param scilabGraph + * the associated graph + */ + public MultiSelectionDependantAction(ScilabGraph scilabGraph) { + super(scilabGraph); + + if (scilabGraph != null) { + SelectedNumberOfCellsConstraint constraint = new SelectedNumberOfCellsConstraint(2); + constraint.install(this, scilabGraph); + } + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/base/OneBlockDependantAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/base/OneBlockDependantAction.java new file mode 100755 index 000000000..993183438 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/base/OneBlockDependantAction.java @@ -0,0 +1,31 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions.base; + +import org.scilab.modules.graph.ScilabGraph; + +/** + * Common class for actions that need at least 1 cell on the graph. + */ +public abstract class OneBlockDependantAction extends DefaultAction { + /** + * Default constructor + * @param scilabGraph the graph to work on + */ + public OneBlockDependantAction(ScilabGraph scilabGraph) { + super(scilabGraph); + + MinimalNumberOfCellsConstraint constraint = new MinimalNumberOfCellsConstraint(1); + constraint.install(this, scilabGraph); + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/base/OneSelectionDependantAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/base/OneSelectionDependantAction.java new file mode 100755 index 000000000..4c1fec61a --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/base/OneSelectionDependantAction.java @@ -0,0 +1,38 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions.base; + +import org.scilab.modules.graph.ScilabGraph; + +/** + * Common class for multi-selection dependent actions. + * + * Children of this class will be activated when there something selected. If + * not, the action will be disabled. + */ +public abstract class OneSelectionDependantAction extends DefaultAction { + /** + * Default constructor + * + * @param scilabGraph + * the associated graph + */ + public OneSelectionDependantAction(ScilabGraph scilabGraph) { + super(scilabGraph); + + if (scilabGraph != null) { + SelectedNumberOfCellsConstraint constraint = new SelectedNumberOfCellsConstraint(1); + constraint.install(this, scilabGraph); + } + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/base/SelectedNumberOfCellsConstraint.java b/modules/graph/src/java/org/scilab/modules/graph/actions/base/SelectedNumberOfCellsConstraint.java new file mode 100755 index 000000000..af47f99db --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/base/SelectedNumberOfCellsConstraint.java @@ -0,0 +1,64 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions.base; + +import org.scilab.modules.graph.ScilabGraph; + +import com.mxgraph.util.mxEvent; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.view.mxGraphSelectionModel; + +/** + * Constraint which specify how many blocks must be selected on the block to + * enable the action. + */ +public class SelectedNumberOfCellsConstraint extends ActionConstraint { + private final int numberOfSelectedCells; + + /** + * Default constructor + * + * @param numberOfSelectedCells + * The number of selected block needed to enable the action. + */ + public SelectedNumberOfCellsConstraint(int numberOfSelectedCells) { + this.numberOfSelectedCells = numberOfSelectedCells; + } + + /** + * Install + * @param action the action + * @param scilabGraph the graph + * @see org.scilab.modules.graph.actions.base.ActionConstraint#install(org.scilab.modules.graph.actions.base.DefaultAction, + * org.scilab.modules.graph.ScilabGraph) + */ + @Override + public void install(DefaultAction action, ScilabGraph scilabGraph) { + super.install(action, scilabGraph); + + scilabGraph.getSelectionModel().addListener(mxEvent.UNDO, this); + } + + /** + * Update the enable state + * @param sender The sender of the event + * @param evt the event + * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object, com.mxgraph.util.mxEventObject) + */ + public void invoke(Object sender, mxEventObject evt) { + mxGraphSelectionModel selection = (mxGraphSelectionModel) sender; + Object[] cells = selection.getCells(); + setEnabled((cells != null) && (cells.length >= numberOfSelectedCells)); + } + +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/base/SpecificCellSelectedConstraint.java b/modules/graph/src/java/org/scilab/modules/graph/actions/base/SpecificCellSelectedConstraint.java new file mode 100755 index 000000000..fb892e3dd --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/base/SpecificCellSelectedConstraint.java @@ -0,0 +1,72 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions.base; + +import org.scilab.modules.graph.ScilabGraph; + +import com.mxgraph.model.mxCell; +import com.mxgraph.util.mxEvent; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.view.mxGraphSelectionModel; + +/** + * Update a menu when a specific kind of cell is selected. + */ +public class SpecificCellSelectedConstraint extends ActionConstraint { + private final Class < ? extends mxCell > kind; + + /** + * Default constructor + * @param kind The kind of cell which enable/disbale the action. + */ + public SpecificCellSelectedConstraint(Class < ? extends mxCell > kind) { + this.kind = kind; + } + + /** + * Install + * + * @param action + * the current action + * @param scilabGraph + * tthe scilab graph + * @see org.scilab.modules.graph.actions.base.ActionConstraint#install(org.scilab.modules.graph.actions.base.DefaultAction, + * org.scilab.modules.graph.ScilabGraph) + */ + @Override + public void install(DefaultAction action, ScilabGraph scilabGraph) { + super.install(action, scilabGraph); + + scilabGraph.getSelectionModel().addListener(mxEvent.UNDO, this); + } + + /** + * @param sender the sender object + * @param evt the event + * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object, com.mxgraph.util.mxEventObject) + */ + public void invoke(Object sender, mxEventObject evt) { + mxGraphSelectionModel selection = (mxGraphSelectionModel) sender; + Object[] cells = selection.getCells(); + + for (Object object : cells) { + if (kind.isInstance(object)) { + setEnabled(true); + return; + } + } + + setEnabled(false); + } + +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/actions/base/VertexSelectionDependantAction.java b/modules/graph/src/java/org/scilab/modules/graph/actions/base/VertexSelectionDependantAction.java new file mode 100755 index 000000000..33b4b1c4a --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/actions/base/VertexSelectionDependantAction.java @@ -0,0 +1,97 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.actions.base; + +import org.scilab.modules.graph.ScilabGraph; + +import com.mxgraph.model.mxCell; +import com.mxgraph.util.mxEvent; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.view.mxGraphSelectionModel; + +/** + * Common class for selection dependent actions. + * + * Children of this class will be activated when there something selected. If + * not, the action will be disabled. + */ +public abstract class VertexSelectionDependantAction extends DefaultAction { + + /** + * Enable the selection if there is at least a vertex in the selection. + */ + private final class VertexSelectionDependantConstraint extends ActionConstraint { + + /** + * Default constructor + */ + public VertexSelectionDependantConstraint() { + super(); + } + + /** + * @param action the action + * @param scilabGraph the current graph + * @see org.scilab.modules.graph.actions.base.ActionConstraint#install(org.scilab.modules.graph.actions.base.DefaultAction, + * org.scilab.modules.graph.ScilabGraph) + */ + @Override + public void install(DefaultAction action, ScilabGraph scilabGraph) { + super.install(action, scilabGraph); + + scilabGraph.getSelectionModel().addListener(mxEvent.UNDO, this); + } + + /** + * @param sender the sender + * @param evt the event + * @see com.mxgraph.util.mxEventSource.mxIEventListener#invoke(java.lang.Object, com.mxgraph.util.mxEventObject) + */ + public void invoke(Object sender, mxEventObject evt) { + mxGraphSelectionModel selection = (mxGraphSelectionModel) sender; + Object[] cells = selection.getCells(); + + boolean vertexFound = false; + if (cells != null) { + for (Object object : cells) { + if (object instanceof mxCell) { + mxCell cell = (mxCell) object; + vertexFound = cell.isVertex(); + } + + if (vertexFound) { + break; + } + } + + setEnabled(vertexFound); + } + } + + } + + /** + * Default constructor + * + * @param scilabGraph + * The associated graph + */ + public VertexSelectionDependantAction(ScilabGraph scilabGraph) { + super(scilabGraph); + + if (scilabGraph != null) { + VertexSelectionDependantConstraint c = new VertexSelectionDependantConstraint(); + c.install(this, scilabGraph); + } + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/event/ArrowKeyListener.java b/modules/graph/src/java/org/scilab/modules/graph/event/ArrowKeyListener.java new file mode 100755 index 000000000..bbda53699 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/event/ArrowKeyListener.java @@ -0,0 +1,212 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Clement DAVID + * Copyright (C) 2012 - Scilab Enterprises - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.event; + +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.List; + +import javax.swing.Timer; + +import com.mxgraph.model.mxGeometry; +import com.mxgraph.model.mxIGraphModel; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.util.mxPoint; +import com.mxgraph.view.mxGraph; + +/** + * Move cells with the arrow keys. + */ +public final class ArrowKeyListener implements KeyListener { + + private static final int DEFAULT_PIXEL_MOVE = 1; + private static final int MODIFIER_FACTOR = 5; + private static final int DEFAULT_DELAY = 800; // milliseconds + + /* Configuration variables */ + private final int pixelMove = DEFAULT_PIXEL_MOVE; + private int delay = DEFAULT_DELAY; + + /* Runtime variables */ + private double xIncrement; + private double yIncrement; + private mxGraph graph; + + private final Timer repetitionTimer; + private final ActionListener doMove = new ActionListener() { + @Override + public void actionPerformed(ActionEvent arg0) { + if (graph != null) { + + final mxIGraphModel model = graph.getModel(); + model.beginUpdate(); + try { + for (Object cell : graph.getSelectionCells()) { + // first increment + graph.translateCell(cell, xIncrement, yIncrement); + + // then align + final mxGeometry geom = model.getGeometry(cell); + model.setGeometry(cell, snap(graph, geom)); + } + } finally { + model.endUpdate(); + } + + } + } + }; + + private static final mxGeometry snap(final mxGraph graph, final mxGeometry rect) { + final double x = graph.snap(rect.getX()); + final double y = graph.snap(rect.getY()); + + final double width = graph.snap(rect.getWidth() - x + rect.getX()); + final double height = graph.snap(rect.getHeight() - y + rect.getY()); + + final List<mxPoint> points = rect.getPoints(); + if (points != null) { + for (final mxPoint p : points) { + p.setX(graph.snap(p.getX())); + p.setY(graph.snap(p.getY())); + } + } + + final mxGeometry snappedGeom = new mxGeometry(x, y, width, height); + snappedGeom.setPoints(points); + + return snappedGeom; + } + + /** + * Constructor + */ + public ArrowKeyListener() { + repetitionTimer = new Timer(delay, doMove); + repetitionTimer.setInitialDelay(0); + } + + /** + * @param delay + * the delay to set + */ + public void setDelay(int delay) { + this.delay = delay; + repetitionTimer.setDelay(delay); + } + + /** + * @return the delay + */ + public int getDelay() { + return delay; + } + + /** + * Get the action parameters and start the action timer. + * + * @param e + * key event + */ + @Override + public void keyPressed(KeyEvent e) { + if (e.isConsumed()) { + return; + } + + int realMove; + boolean mustMove = true; + + mxGraphComponent sourceDiagram = (mxGraphComponent) e.getSource(); + graph = sourceDiagram.getGraph(); + + if (graph.isGridEnabled()) { + realMove = graph.getGridSize(); + } else { + realMove = pixelMove; + } + + if (e.getModifiers() == Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()) { + realMove *= MODIFIER_FACTOR; + } + + switch (e.getKeyCode()) { + case KeyEvent.VK_UP: + yIncrement = -realMove; + break; + + case KeyEvent.VK_DOWN: + yIncrement = realMove; + break; + + case KeyEvent.VK_RIGHT: + xIncrement = realMove; + break; + + case KeyEvent.VK_LEFT: + xIncrement = -realMove; + break; + + default: + mustMove = false; + break; + } + + if (!mustMove) { + return; + } + + if (!graph.isGridEnabled()) { + xIncrement *= sourceDiagram.getZoomFactor(); + yIncrement *= sourceDiagram.getZoomFactor(); + } + + repetitionTimer.start(); + + e.consume(); + } + + /** + * Stop the action timer and clear parameters + * + * @param e + * key event + */ + @Override + public void keyReleased(KeyEvent e) { + if (e.isConsumed()) { + return; + } + + repetitionTimer.stop(); + yIncrement = 0; + xIncrement = 0; + graph = null; + + e.consume(); + } + + /** + * Not used there + * + * @param e + * Not used + */ + @Override + public void keyTyped(KeyEvent e) { + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/io/ScilabBooleanCodec.java b/modules/graph/src/java/org/scilab/modules/graph/io/ScilabBooleanCodec.java new file mode 100755 index 000000000..50163f4a6 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/io/ScilabBooleanCodec.java @@ -0,0 +1,178 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.io; + +import java.util.Map; +import java.util.logging.Logger; + +import org.scilab.modules.types.ScilabBoolean; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import com.mxgraph.io.mxCodec; +import com.mxgraph.io.mxCodecRegistry; + +/** + * Define serialization for a {@link ScilabBoolean} instance. + */ +public class ScilabBooleanCodec extends ScilabObjectCodec { + + private static final String VALUE = "value"; + + /** + * Default constructor + * + * @param template + * Prototypical instance of the object to be encoded/decoded. + * @param exclude + * Optional array of fieldnames to be ignored. + * @param idrefs + * Optional array of fieldnames to be converted to/from + * references. + * @param mapping + * Optional mapping from field- to attributenames. + */ + public ScilabBooleanCodec(Object template, String[] exclude, String[] idrefs, Map<String, String> mapping) { + super(template, exclude, idrefs, mapping); + + } + + /** + * Encodes the specified object and returns a node representing then given + * object. Calls beforeEncode after creating the node and afterEncode with + * the resulting node after processing. + * + * @param enc + * Codec that controls the encoding process. + * @param obj + * Object to be encoded. + * @return Returns the resulting XML node that represents the given object. + */ + @Override + public Node encode(mxCodec enc, Object obj) { + String name = mxCodecRegistry.getName(obj); + Node node = enc.getDocument().createElement(name); + + ScilabBoolean scilabBoolean = (ScilabBoolean) obj; + + if (binary) { + int pos = binaryObjects.size(); + binaryObjects.add(scilabBoolean); + mxCodec.setAttribute(node, BINARY, "true"); + mxCodec.setAttribute(node, POSITION, pos); + + return node; + } + + mxCodec.setAttribute(node, WIDTH, scilabBoolean.getWidth()); + mxCodec.setAttribute(node, HEIGHT, scilabBoolean.getHeight()); + + for (int i = 0; i < scilabBoolean.getHeight(); ++i) { + for (int j = 0; j < scilabBoolean.getWidth(); ++j) { + Node data = enc.getDocument().createElement(DATA); + mxCodec.setAttribute(data, LINE, i); + mxCodec.setAttribute(data, COLUMN, j); + mxCodec.setAttribute(data, VALUE, scilabBoolean.getData()[i][j]); + node.appendChild(data); + } + } + return node; + } + + /** + * Parses the given node into the object or returns a new object + * representing the given node. + * + * @param dec + * Codec that controls the encoding process. + * @param node + * XML node to be decoded. + * @param into + * Optional object to encode the node into. + * @return Returns the resulting object that represents the given XML node + * or the object given to the method as the into parameter. + */ + @Override + public Object decode(mxCodec dec, Node node, Object into) { + ScilabBoolean obj = null; + + try { + if (node.getNodeType() != Node.ELEMENT_NODE) { + throw new UnrecognizeFormatException(); + } + + // attrs = {"as", "height", "width", "binary", "position"} + final NamedNodeMap attrs = node.getAttributes(); + if (attrs == null) { + throw new UnrecognizeFormatException(); + } + + if (getBooleanAttribute(attrs, BINARY)) { + return binaryObjects.get(getIntegerAttribute(attrs, POSITION)); + } + + obj = (ScilabBoolean) cloneTemplate(node); + + final int height = getHeight(attrs); + final int width = getWidth(attrs); + + if (height * width == 0) { + return obj; + } + + final boolean[][] data = new boolean[height][width]; + fillData(node, data); + + obj.setData(data); + + } catch (UnrecognizeFormatException e) { + Logger.getLogger(ScilabStringCodec.class.getName()).severe(e.toString()); + } catch (NumberFormatException e) { + Logger.getLogger(ScilabStringCodec.class.getName()).severe(e.toString()); + } + + return obj; + } + + /** + * Fill the data from the node. + * + * @param node + * the ScilabBoolean node + * @param data + * the allocated data + * @throws UnrecognizeFormatException + * when we are unable to decode the node. + */ + private void fillData(Node node, final boolean[][] data) throws UnrecognizeFormatException { + for (Node n = node.getFirstChild(); n != null; n = n.getNextSibling()) { + if (n.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + final NamedNodeMap dataAttrs = n.getAttributes(); + if (dataAttrs == null) { + throw new UnrecognizeFormatException(); + } + + final int column = getColumnIndex(dataAttrs); + final int line = getLineIndex(dataAttrs); + + final Node v = dataAttrs.getNamedItem(VALUE); + if (v == null) { + throw new UnrecognizeFormatException(); + } + data[line][column] = Boolean.parseBoolean(v.getNodeValue()); + } + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/io/ScilabDoubleCodec.java b/modules/graph/src/java/org/scilab/modules/graph/io/ScilabDoubleCodec.java new file mode 100755 index 000000000..6811b4f67 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/io/ScilabDoubleCodec.java @@ -0,0 +1,202 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.io; + +import java.util.Map; +import java.util.logging.Logger; + +import org.scilab.modules.types.ScilabDouble; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import com.mxgraph.io.mxCodec; +import com.mxgraph.io.mxCodecRegistry; + +/** + * Define serialization for a {@link ScilabDouble} instance. + */ +public class ScilabDoubleCodec extends ScilabObjectCodec { + + private static final String REALPART = "realPart"; + private static final String IMGPART = "imaginaryPart"; + + /** + * Default constructor + * + * @param template + * Prototypical instance of the object to be encoded/decoded. + * @param exclude + * Optional array of fieldnames to be ignored. + * @param idrefs + * Optional array of fieldnames to be converted to/from + * references. + * @param mapping + * Optional mapping from field- to attributenames. + */ + public ScilabDoubleCodec(Object template, String[] exclude, String[] idrefs, Map<String, String> mapping) { + super(template, exclude, idrefs, mapping); + + } + + /** + * Encodes the specified object and returns a node representing then given + * object. Calls beforeEncode after creating the node and afterEncode with + * the resulting node after processing. + * + * @param enc + * Codec that controls the encoding process. + * @param obj + * Object to be encoded. + * @return Returns the resulting XML node that represents the given object. + */ + @Override + public Node encode(mxCodec enc, Object obj) { + String name = mxCodecRegistry.getName(obj); + Node node = enc.getDocument().createElement(name); + + ScilabDouble scilabDouble = (ScilabDouble) obj; + + if (binary) { + int pos = binaryObjects.size(); + binaryObjects.add(scilabDouble); + mxCodec.setAttribute(node, BINARY, "true"); + mxCodec.setAttribute(node, POSITION, pos); + + return node; + } + + mxCodec.setAttribute(node, WIDTH, scilabDouble.getWidth()); + mxCodec.setAttribute(node, HEIGHT, scilabDouble.getHeight()); + + for (int i = 0; i < scilabDouble.getHeight(); ++i) { + for (int j = 0; j < scilabDouble.getWidth(); ++j) { + Node data = enc.getDocument().createElement(DATA); + mxCodec.setAttribute(data, LINE, i); + mxCodec.setAttribute(data, COLUMN, j); + if (scilabDouble.getRealPart() != null) { + mxCodec.setAttribute(data, REALPART, scilabDouble.getRealPart()[i][j]); + } + if (scilabDouble.getImaginaryPart() != null) { + final double[][] imag = scilabDouble.getImaginaryPart(); + if (imag.length > i && imag[i].length > j) { + mxCodec.setAttribute(data, IMGPART, imag[i][j]); + } + } + node.appendChild(data); + } + } + return node; + } + + /** + * Parses the given node into the object or returns a new object + * representing the given node. + * + * @param dec + * Codec that controls the encoding process. + * @param node + * XML node to be decoded. + * @param into + * Optional object to encode the node into. + * @return Returns the resulting object that represents the given XML node + * or the object given to the method as the into parameter. + */ + @Override + public Object decode(mxCodec dec, Node node, Object into) { + ScilabDouble obj = null; + + try { + if (node.getNodeType() != Node.ELEMENT_NODE) { + throw new UnrecognizeFormatException(); + } + + // attrs = {"as", "height", "width", "binary", "position"} + final NamedNodeMap attrs = node.getAttributes(); + if (attrs == null) { + throw new UnrecognizeFormatException(); + } + + if (getBooleanAttribute(attrs, BINARY)) { + return binaryObjects.get(getIntegerAttribute(attrs, POSITION)); + } + + obj = (ScilabDouble) cloneTemplate(node); + + final int height = getHeight(attrs); + final int width = getWidth(attrs); + + if (height * width == 0) { + return obj; + } + + final double[][] realData = new double[height][width]; + final double[][] imgData = new double[height][width]; + final boolean isRealOnly = fillData(node, realData, imgData); + + obj.setRealPart(realData); + if (!isRealOnly) { + obj.setImaginaryPart(imgData); + } + + } catch (UnrecognizeFormatException e) { + Logger.getLogger(ScilabDoubleCodec.class.getName()).severe(e.toString()); + } catch (NumberFormatException e) { + Logger.getLogger(ScilabDoubleCodec.class.getName()).severe(e.toString()); + } + + return obj; + } + + /** + * Fill the data from the node. + * + * @param node + * the ScilabDouble node + * @param realData + * the real data + * @param imgData + * the optional imaginary data + * @return true if the imaginary data is empty, false otherwise. + * @throws UnrecognizeFormatException + * on decoding error + */ + private boolean fillData(Node node, double[][] realData, double[][] imgData) throws UnrecognizeFormatException { + boolean isRealOnly = true; + for (Node n = node.getFirstChild(); n != null; n = n.getNextSibling()) { + if (n.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + final NamedNodeMap dataAttrs = n.getAttributes(); + if (dataAttrs == null) { + throw new UnrecognizeFormatException(); + } + + final int column = getColumnIndex(dataAttrs); + final int line = getLineIndex(dataAttrs); + + final Node real = dataAttrs.getNamedItem(REALPART); + if (real == null) { + throw new UnrecognizeFormatException(); + } + realData[line][column] = Double.parseDouble(real.getNodeValue()); + + final Node img = dataAttrs.getNamedItem(IMGPART); + if (img != null) { + isRealOnly = false; + imgData[line][column] = Double.parseDouble(img.getNodeValue()); + } + } + return isRealOnly; + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/io/ScilabGraphCodec.java b/modules/graph/src/java/org/scilab/modules/graph/io/ScilabGraphCodec.java new file mode 100755 index 000000000..c8ca88cbc --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/io/ScilabGraphCodec.java @@ -0,0 +1,120 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2009 - DIGITEO - Allan SIMON + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.io; + +import java.awt.Color; +import java.util.Map; + +import org.scilab.modules.graph.ScilabGraph; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import com.mxgraph.io.mxCodec; +import com.mxgraph.io.mxObjectCodec; + +/** + * Default codec for the graph component + */ +public class ScilabGraphCodec extends mxObjectCodec { + private static final String BACKGROUND = "background"; + + /** + * Default constructor + * + * @param template + * the object template + */ + public ScilabGraphCodec(Object template) { + super(template); + } + + /** + * The constructor used on for configuration + * + * @param template + * Prototypical instance of the object to be encoded/decoded. + * @param exclude + * Optional array of fieldnames to be ignored. + * @param idrefs + * Optional array of fieldnames to be converted to/from + * references. + * @param mapping + * Optional mapping from field- to attributenames. + */ + public ScilabGraphCodec(Object template, String[] exclude, String[] idrefs, Map<String, String> mapping) { + super(template, exclude, idrefs, mapping); + } + + /** + * Trace any msg to the xml document. + * + * @param enc + * the current encoder + * @param node + * the current node + * @param msg + * the message + * @param format + * the format + */ + protected void trace(mxCodec enc, Node node, String msg, Object... format) { + node.appendChild(enc.getDocument().createComment(String.format(msg, format))); + } + + /** + * Things to do before encoding + * + * @param enc + * Codec that controls the encoding process. + * @param obj + * Object to be encoded. + * @param node + * XML node to encode the object into. + * @return Returns the object to be encoded by the default encoding. + * @see com.mxgraph.io.mxObjectCodec#beforeEncode(com.mxgraph.io.mxCodec, + * java.lang.Object, org.w3c.dom.Node) + */ + @Override + public Object beforeEncode(mxCodec enc, Object obj, Node node) { + final ScilabGraph graph = (ScilabGraph) obj; + if (graph.getAsComponent() != null) { + ((Element) node).setAttribute(BACKGROUND, String.valueOf(graph.getAsComponent().getBackground().getRGB())); + } + return super.beforeEncode(enc, obj, node); + } + + /** + * Apply compatibility pattern to the decoded object + * + * @param dec + * Codec that controls the decoding process. + * @param node + * XML node to decode the object from. + * @param obj + * Object decoded. + * @return The Object transformed + * @see org.scilab.modules.xcos.io.codec.XcosObjectCodec#afterDecode(com.mxgraph.io.mxCodec, + * org.w3c.dom.Node, java.lang.Object) + */ + @Override + public Object afterDecode(mxCodec dec, Node node, Object obj) { + final ScilabGraph graph = (ScilabGraph) obj; + final Element elem = (Element) node; + if (graph.getAsComponent() != null && !elem.getAttribute(BACKGROUND).isEmpty()) { + graph.getAsComponent().setBackground((new Color(Integer.parseInt((((Element) node).getAttribute(BACKGROUND)))))); + } + + return super.afterDecode(dec, node, obj); + } + +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/io/ScilabIntegerCodec.java b/modules/graph/src/java/org/scilab/modules/graph/io/ScilabIntegerCodec.java new file mode 100755 index 000000000..270afc6eb --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/io/ScilabIntegerCodec.java @@ -0,0 +1,332 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.io; + +import java.util.Map; +import java.util.logging.Logger; + +import org.scilab.modules.types.ScilabInteger; +import org.scilab.modules.types.ScilabIntegerTypeEnum; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import com.mxgraph.io.mxCodec; +import com.mxgraph.io.mxCodecRegistry; + +/** + * Define serialization for a {@link ScilabInteger} instance. + */ +public class ScilabIntegerCodec extends ScilabObjectCodec { + + private static final String BUNSIGNED = "bUnsigned"; /* With old IntegerType */ + private static final String VALUE = "value"; + private static final String PREC = "precision"; /* With old IntegerType */ + private static final String PRECISION = "intPrecision"; /* + * with + * ScilabIntegerTypeEnum + */ + + /** + * Default constructor + * + * @param template + * Prototypical instance of the object to be encoded/decoded. + * @param exclude + * Optional array of fieldnames to be ignored. + * @param idrefs + * Optional array of fieldnames to be converted to/from + * references. + * @param mapping + * Optional mapping from field- to attributenames. + */ + public ScilabIntegerCodec(Object template, String[] exclude, String[] idrefs, Map<String, String> mapping) { + super(template, exclude, idrefs, mapping); + + } + + /** + * Encodes the specified object and returns a node representing then given + * object. Calls beforeEncode after creating the node and afterEncode with + * the resulting node after processing. + * + * @param enc + * Codec that controls the encoding process. + * @param obj + * Object to be encoded. + * @return Returns the resulting XML node that represents the given object. + */ + @Override + public Node encode(mxCodec enc, Object obj) { + String name = mxCodecRegistry.getName(obj); + Node node = enc.getDocument().createElement(name); + + ScilabInteger scilabInteger = (ScilabInteger) obj; + + if (binary) { + int pos = binaryObjects.size(); + binaryObjects.add(scilabInteger); + mxCodec.setAttribute(node, BINARY, "true"); + mxCodec.setAttribute(node, POSITION, pos); + + return node; + } + + mxCodec.setAttribute(node, WIDTH, scilabInteger.getWidth()); + mxCodec.setAttribute(node, HEIGHT, scilabInteger.getHeight()); + mxCodec.setAttribute(node, PRECISION, scilabInteger.getPrec().name()); + + for (int i = 0; i < scilabInteger.getHeight(); ++i) { + for (int j = 0; j < scilabInteger.getWidth(); ++j) { + Node data = enc.getDocument().createElement(DATA); + mxCodec.setAttribute(data, LINE, i); + mxCodec.setAttribute(data, COLUMN, j); + mxCodec.setAttribute(data, VALUE, scilabInteger.getData()[i][j]); + node.appendChild(data); + } + } + return node; + } + + /** + * Parses the given node into the object or returns a new object + * representing the given node. + * + * @param dec + * Codec that controls the encoding process. + * @param node + * XML node to be decoded. + * @param into + * Optional object to encode the node into. + * @return Returns the resulting object that represents the given XML node + * or the object given to the method as the into parameter. + */ + @Override + public Object decode(mxCodec dec, Node node, Object into) { + ScilabInteger obj = null; + + try { + if (node.getNodeType() != Node.ELEMENT_NODE) { + throw new UnrecognizeFormatException(); + } + + // attrs = {"as", "height", "width", "binary", "position"} + final NamedNodeMap attrs = node.getAttributes(); + if (attrs == null) { + throw new UnrecognizeFormatException(); + } + + if (getBooleanAttribute(attrs, BINARY)) { + return binaryObjects.get(getIntegerAttribute(attrs, POSITION)); + } + + obj = (ScilabInteger) cloneTemplate(node); + + final int height = getHeight(attrs); + final int width = getWidth(attrs); + + if (height * width == 0) { + return obj; + } + + /* Default values */ + final Node precNode = attrs.getNamedItem(PRECISION); + ScilabIntegerTypeEnum precision; + if (precNode != null) { + precision = ScilabIntegerTypeEnum.valueOf(precNode.getNodeValue()); + } else { + precision = ScilabIntegerTypeEnum.sci_uint8; + } + + /* Compatibility for pre-5.2.2 version */ + final Node prec = attrs.getNamedItem(PREC); + if (prec != null) { + /* Old version, we have to convert to the new one */ + final Node u = attrs.getNamedItem(BUNSIGNED); + final boolean unsigned; + /* + * the default boolean value is false, this value is not + * serialized by jgraphx this if we doesn't have attribute the + * value is "false". + */ + unsigned = u != null; + + precision = ScilabInteger.convertOldType(prec.getNodeValue(), unsigned); + + } + + switch (precision) { + case sci_int8: + case sci_uint8: + final byte[][] data8 = new byte[height][width]; + fillData(node, data8); + obj.setData(data8, precision == ScilabIntegerTypeEnum.sci_uint8); + break; + case sci_int16: + case sci_uint16: + final short[][] data16 = new short[height][width]; + fillData(node, data16); + obj.setData(data16, precision == ScilabIntegerTypeEnum.sci_uint16); + break; + case sci_int32: + case sci_uint32: + final int[][] data32 = new int[height][width]; + fillData(node, data32); + obj.setData(data32, precision == ScilabIntegerTypeEnum.sci_uint32); + break; + default: + final long[][] data64 = new long[height][width]; + fillData(node, data64); + obj.setData(data64, precision == ScilabIntegerTypeEnum.sci_uint64); + break; + } + + } catch (UnrecognizeFormatException e) { + Logger.getLogger(ScilabIntegerCodec.class.getName()).severe(e.toString()); + } catch (NumberFormatException e) { + Logger.getLogger(ScilabIntegerCodec.class.getName()).severe(e.toString()); + } + return obj; + } + + /** + * Fill the data from the node. + * + * @param node + * the ScilabInteger node + * @param data + * the allocated data + * @throws UnrecognizeFormatException + * when we are unable to decode the node. + */ + private void fillData(Node node, byte[][] data) throws UnrecognizeFormatException { + for (Node n = node.getFirstChild(); n != null; n = n.getNextSibling()) { + if (n.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + final NamedNodeMap dataAttrs = n.getAttributes(); + if (dataAttrs == null) { + throw new UnrecognizeFormatException(); + } + + final int column = getColumnIndex(dataAttrs); + final int line = getLineIndex(dataAttrs); + + final Node v = dataAttrs.getNamedItem(VALUE); + if (v == null) { + throw new UnrecognizeFormatException(); + } + + data[line][column] = Byte.parseByte(v.getNodeValue()); + } + } + + /** + * Fill the data from the node. + * + * @param node + * the ScilabInteger node + * @param data + * the allocated data + * @throws UnrecognizeFormatException + * when we are unable to decode the node. + */ + private void fillData(Node node, short[][] data) throws UnrecognizeFormatException { + for (Node n = node.getFirstChild(); n != null; n = n.getNextSibling()) { + if (n.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + final NamedNodeMap dataAttrs = n.getAttributes(); + if (dataAttrs == null) { + throw new UnrecognizeFormatException(); + } + + final int column = getColumnIndex(dataAttrs); + final int line = getLineIndex(dataAttrs); + + final Node v = dataAttrs.getNamedItem(VALUE); + if (v == null) { + throw new UnrecognizeFormatException(); + } + + data[line][column] = Short.parseShort(v.getNodeValue()); + } + } + + /** + * Fill the data from the node. + * + * @param node + * the ScilabInteger node + * @param data + * the allocated data + * @throws UnrecognizeFormatException + * when we are unable to decode the node. + */ + private void fillData(Node node, int[][] data) throws UnrecognizeFormatException { + for (Node n = node.getFirstChild(); n != null; n = n.getNextSibling()) { + if (n.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + final NamedNodeMap dataAttrs = n.getAttributes(); + if (dataAttrs == null) { + throw new UnrecognizeFormatException(); + } + + final int column = getColumnIndex(dataAttrs); + final int line = getLineIndex(dataAttrs); + + final Node v = dataAttrs.getNamedItem(VALUE); + if (v == null) { + throw new UnrecognizeFormatException(); + } + + data[line][column] = Integer.parseInt(v.getNodeValue()); + } + } + + /** + * Fill the data from the node. + * + * @param node + * the ScilabInteger node + * @param data + * the allocated data + * @throws UnrecognizeFormatException + * when we are unable to decode the node. + */ + private void fillData(Node node, long[][] data) throws UnrecognizeFormatException { + for (Node n = node.getFirstChild(); n != null; n = n.getNextSibling()) { + if (n.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + final NamedNodeMap dataAttrs = n.getAttributes(); + if (dataAttrs == null) { + throw new UnrecognizeFormatException(); + } + + final int column = getColumnIndex(dataAttrs); + final int line = getLineIndex(dataAttrs); + + final Node v = dataAttrs.getNamedItem(VALUE); + if (v == null) { + throw new UnrecognizeFormatException(); + } + + data[line][column] = Long.parseLong(v.getNodeValue()); + } + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/io/ScilabListCodec.java b/modules/graph/src/java/org/scilab/modules/graph/io/ScilabListCodec.java new file mode 100755 index 000000000..e4b429f65 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/io/ScilabListCodec.java @@ -0,0 +1,178 @@ +/* + * Scilab (http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2009 - DIGITEO - Allan SIMON + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.io; + +import java.util.Map; +import java.util.logging.Logger; + +import org.scilab.modules.types.ScilabList; +import org.scilab.modules.types.ScilabMList; +import org.scilab.modules.types.ScilabTList; +import org.scilab.modules.types.ScilabType; + +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import com.mxgraph.io.mxCodec; +import com.mxgraph.io.mxCodecRegistry; + +/** + * Define serialization for a {@link ScilabList} instance. + */ +public class ScilabListCodec extends ScilabObjectCodec { + + private static final String SCILAB_CLASS = "scilabClass"; + + /** + * Default constructor + * + * @param template + * Prototypical instance of the object to be encoded/decoded. + * @param exclude + * Optional array of fieldnames to be ignored. + * @param idrefs + * Optional array of fieldnames to be converted to/from + * references. + * @param mapping + * Optional mapping from field- to attributenames. + */ + public ScilabListCodec(Object template, String[] exclude, String[] idrefs, Map<String, String> mapping) { + super(template, exclude, idrefs, mapping); + + } + + /** + * Add the class name as an attribute. + * + * @param enc + * Codec that controls the encoding process. + * @param obj + * Object to be encoded. + * @param node + * XML node that represents the default encoding. + * @return Returns the resulting node of the encoding. + * @see com.mxgraph.io.mxObjectCodec#beforeEncode(com.mxgraph.io.mxCodec, + * java.lang.Object, org.w3c.dom.Node) + */ + public Object beforeEncode(mxCodec enc, Object obj, Node node) { + if (!binary) { + String type = ""; + for (Class <? extends Object > klass = obj.getClass(); klass != Object.class; klass = klass.getSuperclass()) { + if (klass == ScilabMList.class || klass == ScilabTList.class || klass == ScilabList.class || klass.isArray()) { + type = klass.getSimpleName(); + break; + } + } + + assert !type.equals(""); + mxCodec.setAttribute(node, SCILAB_CLASS, type); + } + + return obj; + } + + /** + * Instantiate defined class for the attribute + * + * @param node + * the node we are working on + * @return the instance of the node. + * @see com.mxgraph.io.mxObjectCodec#cloneTemplate(org.w3c.dom.Node) + */ + public Object cloneTemplate(Node node) { + Object obj = null; + if (node.getAttributes().getNamedItem(SCILAB_CLASS) != null) { + String scilabClass = node.getAttributes().getNamedItem(SCILAB_CLASS).getNodeValue(); + if (scilabClass.equalsIgnoreCase("ScilabMList")) { + obj = new ScilabMList(); + } else if (scilabClass.equalsIgnoreCase("ScilabTList")) { + obj = new ScilabTList(); + } else if (scilabClass.equalsIgnoreCase("ScilabList")) { + obj = new ScilabList(); + } else { + + obj = super.cloneTemplate(node); + } + + } else { + obj = super.cloneTemplate(node); + } + + return obj; + } + + /** + * Encodes the specified object and returns a node representing then given + * object. Calls beforeEncode after creating the node and afterEncode with + * the resulting node after processing. + * + * @param enc + * Codec that controls the encoding process. + * @param obj + * Object to be encoded. + * @return Returns the resulting XML node that represents the given object. + */ + @Override + public Node encode(mxCodec enc, Object obj) { + String name = mxCodecRegistry.getName(obj); + Node node = enc.getDocument().createElement(name); + + if (binary && (obj instanceof ScilabType)) { + int pos = binaryObjects.size(); + binaryObjects.add((ScilabType) obj); + mxCodec.setAttribute(node, BINARY, "true"); + mxCodec.setAttribute(node, POSITION, pos); + + return node; + } + + return super.encode(enc, obj); + } + + /** + * Workaround for a jgraphx bug on deserialization with a possible abstract + * array and default value. + * + * @param dec + * the current decoder instance + * @param node + * the current node + * @param into + * the object decode into (may be a wrongly typed instance) + * @return a valid (right typed) instance + * @see com.mxgraph.io.mxObjectCodec#decode(com.mxgraph.io.mxCodec, + * org.w3c.dom.Node, java.lang.Object) + * @see http://www.jgraph.org/bugzilla/show_bug.cgi?id=55 + * @see http://bugzilla.scilab.org/show_bug.cgi?id=8141 + */ + @Override + public Object decode(mxCodec dec, Node node, Object into) { + try { + final NamedNodeMap attrs = node.getAttributes(); + if (attrs != null && getBooleanAttribute(attrs, BINARY)) { + return binaryObjects.get(getIntegerAttribute(attrs, POSITION)); + } + } catch (UnrecognizeFormatException e) { + Logger.getLogger(ScilabDoubleCodec.class.getName()).severe(e.toString()); + } + + // Workaround case selection : + // - node is an "Array" + // - into (the default template) is not. + if (node.getNodeName().equals("Array") && into != null && !into.getClass().isArray()) { + return super.decode(dec, node, null); + } else { + return super.decode(dec, node, into); + } + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/io/ScilabObjectCodec.java b/modules/graph/src/java/org/scilab/modules/graph/io/ScilabObjectCodec.java new file mode 100755 index 000000000..9ddb8397a --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/io/ScilabObjectCodec.java @@ -0,0 +1,237 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.io; + +import java.util.HashSet; +import java.util.Map; + +import org.scilab.modules.types.ScilabBoolean; +import org.scilab.modules.types.ScilabDouble; +import org.scilab.modules.types.ScilabInteger; +import org.scilab.modules.types.ScilabList; +import org.scilab.modules.types.ScilabString; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import com.mxgraph.io.mxCodecRegistry; +import com.mxgraph.io.mxObjectCodec; + +/** + * Codec for any Scilab object + */ +public abstract class ScilabObjectCodec extends mxObjectCodec { + /** Height of the data */ + protected static final String HEIGHT = "height"; + /** Width of the data */ + protected static final String WIDTH = "width"; + /** columns index of the data */ + protected static final String COLUMN = "column"; + /** line index of the data */ + protected static final String LINE = "line"; + /** Data field */ + protected static final String DATA = "data"; + + protected static final String POSITION = "position"; + protected static final String BINARY = "binary"; + + protected static boolean binary; + protected static ScilabList binaryObjects; + protected static final Object LOCK = new Object();; + + /** + * Throw when we cannot load the XML. + */ + @SuppressWarnings(value = { "serial" }) + public class UnrecognizeFormatException extends Exception { + /** + * Default constructor + */ + public UnrecognizeFormatException() { + super(); + } + + /** + * Constructor from another exception + * + * @param cause + * the source exception. + */ + public UnrecognizeFormatException(Exception cause) { + super(cause); + } + } + + /** + * The constructor used on the configuration + * + * @param template + * Prototypical instance of the object to be encoded/decoded. + * @param exclude + * Optional array of fieldnames to be ignored. + * @param idrefs + * Optional array of fieldnames to be converted to/from + * references. + * @param mapping + * Optional mapping from field- to attributenames. + */ + public ScilabObjectCodec(Object template, String[] exclude, String[] idrefs, Map<String, String> mapping) { + super(template, exclude, idrefs, mapping); + + final HashSet<String> newExclude = new HashSet<String>(this.exclude); + newExclude.add("type"); + this.exclude = newExclude; + + } + + public static Object enableBinarySerialization(ScilabList list) { + synchronized (LOCK) { + binary = true; + if (list == null) { + binaryObjects = new ScilabList(); + } else { + binaryObjects = list; + } + + return LOCK; + } + } + + public static ScilabList disableBinarySerialization() { + synchronized (LOCK) { + binary = false; + ScilabList ret = binaryObjects; + binaryObjects = null; + + return ret; + } + } + + public static ScilabList getBinaryObjects() { + return binaryObjects; + } + + /** + * Register all known codecs on the {@link mxCodecRegistry}. + */ + public static void register() { + ScilabObjectCodec scilabStringCodec = new ScilabStringCodec(new ScilabString(), null, null, null); + mxCodecRegistry.register(scilabStringCodec); + ScilabObjectCodec scilabBooleanCodec = new ScilabBooleanCodec(new ScilabBoolean(), null, null, null); + mxCodecRegistry.register(scilabBooleanCodec); + ScilabObjectCodec scilabDoubleCodec = new ScilabDoubleCodec(new ScilabDouble(), null, null, null); + mxCodecRegistry.register(scilabDoubleCodec); + ScilabObjectCodec scilabIntegerCodec = new ScilabIntegerCodec(new ScilabInteger(), null, null, null); + mxCodecRegistry.register(scilabIntegerCodec); + + ScilabObjectCodec scilabListCodec = new ScilabListCodec(new ScilabList(), new String[] { "scilabClass" }, null, null); + mxCodecRegistry.register(scilabListCodec); + } + + /** + * get an integer value from a attributes. + * + * @param dataAttrs + * the attributes + * @param attribute + * the key to search + * @return the value + * @throws UnrecognizeFormatException + * when the key is not valid. + */ + protected int getIntegerAttribute(final NamedNodeMap dataAttrs, final String attribute) throws UnrecognizeFormatException { + final Node node = dataAttrs.getNamedItem(attribute); + if (node == null) { + throw new UnrecognizeFormatException(); + } + + final int value; + try { + value = Integer.parseInt(node.getNodeValue()); + } catch (NumberFormatException e) { + throw new UnrecognizeFormatException(e); + } + return value; + } + + /** + * get an integer value from a attributes. + * + * @param dataAttrs + * the attributes + * @param attribute + * the key to search + * @return the value + * @throws UnrecognizeFormatException + * when the key is not valid. + */ + protected boolean getBooleanAttribute(final NamedNodeMap dataAttrs, final String attribute) { + final Node node = dataAttrs.getNamedItem(attribute); + if (node == null) { + return false; + } + + return Boolean.parseBoolean(node.getNodeValue()); + } + + /** + * Get the height of the data attributes. + * + * @param attrs + * the data attributes + * @return the height + * @throws UnrecognizeFormatException + * when the height hasn't been found. + */ + protected int getHeight(final NamedNodeMap attrs) throws UnrecognizeFormatException { + return getIntegerAttribute(attrs, HEIGHT); + } + + /** + * Get the width of the data attributes. + * + * @param attrs + * the data attributes + * @return the width + * @throws UnrecognizeFormatException + * when the width hasn't been found. + */ + protected int getWidth(final NamedNodeMap attrs) throws UnrecognizeFormatException { + return getIntegerAttribute(attrs, WIDTH); + } + + /** + * Get the column index of the data attributes. + * + * @param dataAttrs + * the data attributes + * @return the column index + * @throws UnrecognizeFormatException + * when the column index hasn't been found. + */ + protected int getColumnIndex(final NamedNodeMap dataAttrs) throws UnrecognizeFormatException { + return getIntegerAttribute(dataAttrs, COLUMN); + } + + /** + * Get the line index of the data attributes. + * + * @param dataAttrs + * the data attributes + * @return the column index + * @throws UnrecognizeFormatException + * when the column index hasn't been found. + */ + protected int getLineIndex(final NamedNodeMap dataAttrs) throws UnrecognizeFormatException { + return getIntegerAttribute(dataAttrs, LINE); + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/io/ScilabStringCodec.java b/modules/graph/src/java/org/scilab/modules/graph/io/ScilabStringCodec.java new file mode 100755 index 000000000..90f666d7d --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/io/ScilabStringCodec.java @@ -0,0 +1,178 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009-2009 - DIGITEO - Bruno JOFRET + * Copyright (C) 2010-2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.io; + +import java.util.Map; +import java.util.logging.Logger; + +import org.scilab.modules.types.ScilabString; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +import com.mxgraph.io.mxCodec; +import com.mxgraph.io.mxCodecRegistry; + +/** + * Define serialization for a {@link ScilabString} instance. + */ +public class ScilabStringCodec extends ScilabObjectCodec { + + private static final String VALUE = "value"; + + /** + * Default constructor + * + * @param template + * Prototypical instance of the object to be encoded/decoded. + * @param exclude + * Optional array of fieldnames to be ignored. + * @param idrefs + * Optional array of fieldnames to be converted to/from + * references. + * @param mapping + * Optional mapping from field- to attributenames. + */ + public ScilabStringCodec(Object template, String[] exclude, String[] idrefs, Map<String, String> mapping) { + super(template, exclude, idrefs, mapping); + } + + /** + * Encodes the specified object and returns a node representing then given + * object. Calls beforeEncode after creating the node and afterEncode with + * the resulting node after processing. + * + * @param enc + * Codec that controls the encoding process. + * @param obj + * Object to be encoded. + * @return Returns the resulting XML node that represents the given object. + */ + @Override + public Node encode(mxCodec enc, Object obj) { + String name = mxCodecRegistry.getName(obj); + Node node = enc.getDocument().createElement(name); + + ScilabString scilabString = (ScilabString) obj; + + if (binary) { + int pos = binaryObjects.size(); + binaryObjects.add(scilabString); + mxCodec.setAttribute(node, BINARY, "true"); + mxCodec.setAttribute(node, POSITION, pos); + + return node; + } + + mxCodec.setAttribute(node, WIDTH, scilabString.getWidth()); + mxCodec.setAttribute(node, HEIGHT, scilabString.getHeight()); + + for (int i = 0; i < scilabString.getHeight(); ++i) { + for (int j = 0; j < scilabString.getWidth(); ++j) { + Node data = enc.getDocument().createElement(DATA); + mxCodec.setAttribute(data, LINE, i); + mxCodec.setAttribute(data, COLUMN, j); + mxCodec.setAttribute(data, VALUE, scilabString.getData()[i][j]); + node.appendChild(data); + } + } + return node; + } + + /** + * Parses the given node into the object or returns a new object + * representing the given node. + * + * @param dec + * Codec that controls the encoding process. + * @param node + * XML node to be decoded. + * @param into + * Optional object to encode the node into. + * @return Returns the resulting object that represents the given XML node + * or the object given to the method as the into parameter. + */ + @Override + public Object decode(mxCodec dec, Node node, Object into) { + ScilabString obj = null; + + try { + if (node.getNodeType() != Node.ELEMENT_NODE) { + throw new UnrecognizeFormatException(); + } + + // attrs = {"as", "height", "width", "binary", "position"} + final NamedNodeMap attrs = node.getAttributes(); + if (attrs == null) { + throw new UnrecognizeFormatException(); + } + + if (getBooleanAttribute(attrs, BINARY)) { + return binaryObjects.get(getIntegerAttribute(attrs, POSITION)); + } + + obj = (ScilabString) cloneTemplate(node); + + final int height = getHeight(attrs); + final int width = getWidth(attrs); + + if (height * width == 0) { + return obj; + } + + final String[][] data = new String[height][width]; + fillData(node, data); + + obj.setData(data); + + } catch (UnrecognizeFormatException e) { + Logger.getLogger(ScilabStringCodec.class.getName()).severe(e.toString()); + } catch (NumberFormatException e) { + Logger.getLogger(ScilabStringCodec.class.getName()).severe(e.toString()); + } + + return obj; + } + + /** + * Fill the data from the node. + * + * @param node + * the ScilabString node + * @param data + * the allocated data + * @throws UnrecognizeFormatException + * when we are unable to decode the node. + */ + private void fillData(Node node, final String[][] data) throws UnrecognizeFormatException { + for (Node n = node.getFirstChild(); n != null; n = n.getNextSibling()) { + if (n.getNodeType() != Node.ELEMENT_NODE) { + continue; + } + + final NamedNodeMap dataAttrs = n.getAttributes(); + if (dataAttrs == null) { + throw new UnrecognizeFormatException(); + } + + final int column = getColumnIndex(dataAttrs); + final int line = getLineIndex(dataAttrs); + + final Node v = dataAttrs.getNamedItem(VALUE); + if (v == null) { + throw new UnrecognizeFormatException(); + } + data[line][column] = v.getNodeValue(); + } + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/shape/LatexTextShape.java b/modules/graph/src/java/org/scilab/modules/graph/shape/LatexTextShape.java new file mode 100755 index 000000000..a4c495cd4 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/shape/LatexTextShape.java @@ -0,0 +1,115 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.shape; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.util.Map; + +import javax.swing.CellRendererPane; +import javax.swing.Icon; + +import org.scilab.modules.graph.utils.ScilabGraphUtils; + +import com.mxgraph.canvas.mxGraphics2DCanvas; +import com.mxgraph.shape.mxITextShape; +import com.mxgraph.util.mxConstants; +import com.mxgraph.util.mxRectangle; +import com.mxgraph.util.mxUtils; +import com.mxgraph.view.mxCellState; + +/** + * Implement a text shape that can draw LaTEX text. + */ +public class LatexTextShape implements mxITextShape { + + /** + * Painter + * + * @param canvas + * the current canvas + * @param text + * the text to render + * @param state + * the current state + * @param style + * the current style + * @see com.mxgraph.shape.mxITextShape#paintShape(com.mxgraph.canvas.mxGraphics2DCanvas, + * java.lang.String, com.mxgraph.util.mxRectangle, java.util.Map) + */ + @Override + public void paintShape(mxGraphics2DCanvas canvas, String text, mxCellState state, Map<String, Object> style) { + + CellRendererPane rendererPane = canvas.getRendererPane(); + mxRectangle rect = state.getLabelBounds(); + Graphics2D g = canvas.getGraphics(); + + if (rendererPane != null && (g.getClipBounds() == null || g.getClipBounds().intersects(rect.getRectangle()))) { + final double scale = canvas.getScale(); + final double x = rect.getX(); + final double y = rect.getY(); + final double w = rect.getWidth(); + final double h = rect.getHeight(); + + AffineTransform at = g.getTransform(); + + // handle text color + Color textColor = mxUtils.getColor(style, mxConstants.STYLE_FONTCOLOR, Color.BLACK); + rendererPane.setForeground(textColor); + + // TODO: handle horizontal align + final Object align = mxUtils.getString(style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER); + final double horizAlignProportion; + if (align.equals(mxConstants.ALIGN_LEFT)) { + horizAlignProportion = 0; + } else if (align.equals(mxConstants.ALIGN_RIGHT)) { + horizAlignProportion = 1.0; + } else { + horizAlignProportion = 0.5; + } + + // TODO: handle vertical align + final Object vertAlign = mxUtils.getString(style, mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE); + final double vertAlignProportion; + if (vertAlign.equals(mxConstants.ALIGN_TOP)) { + vertAlignProportion = 0; + } else if (vertAlign.equals(mxConstants.ALIGN_BOTTOM)) { + vertAlignProportion = 1.0; + } else { + vertAlignProportion = 0.5; + } + + // parse the text and cache it if valid. Will throw an exception + // if the text is not valid but the text must have been already + // checked on ScilabCanvas#getTextShape(...). + float fontSize = (float) (mxUtils.getInt(style, mxConstants.STYLE_FONTSIZE, 16) * scale); + final Icon icon = ScilabGraphUtils.getTexIcon(text, fontSize); + // the icon is generated scaled so width and height are already scaled + final double ih = icon.getIconHeight(); + final double iw = icon.getIconWidth(); + + + double dx = (iw - w) / 2; + double dy = (ih - h) / 2; + + icon.paintIcon(rendererPane, g, + (int) (x + dx) + mxConstants.LABEL_INSET , + (int) (y + dy) + mxConstants.LABEL_INSET); + + // Restores the previous transformation + g.setTransform(at); + } + } + +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/shape/MathMLTextShape.java b/modules/graph/src/java/org/scilab/modules/graph/shape/MathMLTextShape.java new file mode 100755 index 000000000..8608da2b7 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/shape/MathMLTextShape.java @@ -0,0 +1,89 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.shape; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.util.Map; +import java.util.logging.Logger; + +import javax.swing.CellRendererPane; + +import org.scilab.modules.graph.utils.MathMLRenderUtils; +import org.xml.sax.SAXException; + +import com.mxgraph.canvas.mxGraphics2DCanvas; +import com.mxgraph.shape.mxITextShape; +import com.mxgraph.util.mxConstants; +import com.mxgraph.util.mxUtils; +import com.mxgraph.view.mxCellState; + +/** + * Implement a text shape that can draw MathML text. + * + */ +public class MathMLTextShape implements mxITextShape { + + /** + * Painter + * + * @param canvas + * the current canvas + * @param text + * the text to render + * @param state + * the current state + * @param style + * the current style + * @see com.mxgraph.shape.mxITextShape#paintShape(com.mxgraph.canvas.mxGraphics2DCanvas, + * java.lang.String, com.mxgraph.util.mxRectangle, java.util.Map) + */ + @Override + public void paintShape(mxGraphics2DCanvas canvas, String text, mxCellState state, Map<String, Object> style) { + + CellRendererPane rendererPane = canvas.getRendererPane(); + Rectangle rect = state.getRectangle(); + Graphics2D g = canvas.getGraphics(); + + if (rendererPane != null && (g.getClipBounds() == null || g.getClipBounds().intersects(rect))) { + double scale = canvas.getScale(); + int x = rect.x; + int y = rect.y; + int w = rect.width; + int h = rect.height; + + if (g.hitClip(x, y, w, h)) { + AffineTransform at = g.getTransform(); + + g.scale(scale, scale); + Color textColor = mxUtils.getColor(style, mxConstants.STYLE_FONTCOLOR, Color.BLACK); + rendererPane.setForeground(textColor); + + try { + Component comp = MathMLRenderUtils.getMathMLComponent(text); + rendererPane.paintComponent(g, comp, rendererPane, (int) (x / scale) + mxConstants.LABEL_INSET, + (int) (y / scale) + mxConstants.LABEL_INSET, (int) (w / scale), (int) (h / scale), true); + + // Restores the previous transformation + g.setTransform(at); + } catch (SAXException e) { + Logger.getLogger(MathMLTextShape.class.getName()).severe(e.toString()); + } + } + } + } + +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/shape/SvgShape.java b/modules/graph/src/java/org/scilab/modules/graph/shape/SvgShape.java new file mode 100755 index 000000000..0365f1219 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/shape/SvgShape.java @@ -0,0 +1,85 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010-2010 - DIGITEO - Clement DAVID <clement.david@scilab.org> + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.shape; + +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.geom.AffineTransform; + +import org.apache.batik.ext.awt.RenderingHintsKeyExt; +import org.scilab.modules.graph.ScilabCanvas; + +import com.mxgraph.canvas.mxGraphics2DCanvas; +import com.mxgraph.shape.mxLabelShape; +import com.mxgraph.util.mxConstants; +import com.mxgraph.util.mxUtils; +import com.mxgraph.view.mxCellState; + +/** + * Add the SVG capability to the standard Image shape + */ +public class SvgShape extends mxLabelShape { + /** + * Paint the shape + * + * @param canvas the current canvas + * @param state the current state + * @see com.mxgraph.shape.mxImageShape#paintShape(com.mxgraph.canvas.mxGraphics2DCanvas, com.mxgraph.view.mxCellState) + */ + @Override + public void paintShape(mxGraphics2DCanvas canvas, mxCellState state) { + // paint previously set background + super.paintShape(canvas, state); + + final String image = getImageForStyle(canvas, state); + if (image != null && image.endsWith(".svg")) { + // Remove the "Graphics2D from BufferedImage lacks BUFFERED_IMAGE hint" + // message and tweak Batik rendering options to increase performance. + canvas.getGraphics().setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + canvas.getGraphics().setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + canvas.getGraphics().setRenderingHint(RenderingHintsKeyExt.KEY_TRANSCODING, RenderingHintsKeyExt.VALUE_TRANSCODING_PRINTING); + + final Rectangle rect = getImageBounds(canvas, state); + canvas.getGraphics().translate(rect.x, rect.y); + + ((ScilabCanvas) canvas).paintSvgForegroundImage(rect.width, rect.height, image); + + if (mxUtils.isTrue(state.getStyle(), mxConstants.STYLE_GLASS, false)) { + drawGlassEffect(canvas, state); + } + } else { + super.paintShape(canvas, state); + } + } + + /** + * Get the image bounds for an SVG image + * + * @param canvas the current canvas + * @param state the current state + * @return the bounds of the image + * @see com.mxgraph.shape.mxImageShape#getImageBounds(com.mxgraph.canvas.mxGraphics2DCanvas, com.mxgraph.view.mxCellState) + */ + @Override + public Rectangle getImageBounds(mxGraphics2DCanvas canvas, mxCellState state) { + final double rotation = mxUtils.getDouble(state.getStyle(), + mxConstants.STYLE_ROTATION, 0); + final AffineTransform transform = new AffineTransform(); + transform.rotate(Math.toRadians(rotation), state.getCenterX(), + state.getCenterY()); + final Shape shape = transform.createTransformedShape(state + .getRectangle()); + return shape.getBounds(); + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/utils/Font.java b/modules/graph/src/java/org/scilab/modules/graph/utils/Font.java new file mode 100755 index 000000000..27719f978 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/utils/Font.java @@ -0,0 +1,181 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) DIGITEO - 2010-2010 - Clement DAVID <clement.david@scilab.org> + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.utils; + +import java.awt.Color; + +import com.mxgraph.util.mxConstants; + +/** + * Font list from Scilab xlfont(). + */ +public enum Font { + /** xlfont(0) */ + COURIER(java.awt.Font.MONOSPACED), + /** xlfont(1) */ + SYMBOL(java.awt.Font.DIALOG), + /** xlfont(2) */ + SERIF(java.awt.Font.SERIF), + /** xlfont(3) */ + SERIF_ITALIC(java.awt.Font.SERIF, mxConstants.FONT_ITALIC), + /** xlfont(4) */ + SERIF_BOLD(java.awt.Font.SERIF, mxConstants.FONT_BOLD), + /** xlfont(5) */ + SERIF_BOLD_ITALIC(java.awt.Font.SERIF, mxConstants.FONT_BOLD + + mxConstants.FONT_ITALIC), + /** xlfont(6) */ + SANS_SERIF(java.awt.Font.SANS_SERIF), + /** xlfont(7) */ + SANS_SERIF_ITALIC(java.awt.Font.SANS_SERIF, mxConstants.FONT_ITALIC), + /** xlfont(8) */ + SANS_SERIF_BOLD(java.awt.Font.SANS_SERIF, mxConstants.FONT_BOLD), + /** xlfont(9) */ + SANS_SERIF_BOLD_ITALIC(java.awt.Font.SANS_SERIF, mxConstants.FONT_BOLD + + mxConstants.FONT_ITALIC); + + private static final Color[] COLORMAP = new Color[] { + Color.BLACK, + Color.BLUE, Color.GREEN, Color.CYAN, Color.RED, Color.MAGENTA, + Color.YELLOW, Color.WHITE, new Color(0, 0, 144), + new Color(0, 0, 176), new Color(0, 0, 208), + new Color(135, 206, 255), new Color(0, 144, 0), + new Color(0, 176, 0), new Color(0, 208, 0), new Color(0, 144, 0), + new Color(0, 144, 144), new Color(0, 176, 176), + new Color(0, 208, 208), new Color(144, 0, 0), new Color(176, 0, 0), + new Color(208, 0, 0), new Color(144, 0, 144), + new Color(176, 0, 176), new Color(208, 0, 208), + new Color(128, 48, 0), new Color(160, 64, 0), + new Color(192, 96, 0), new Color(255, 128, 128), + new Color(255, 160, 160), new Color(255, 192, 192), + new Color(255, 224, 224), new Color(255, 215, 0) + }; + + private final String name; + private final int modifiers; + + /** + * Default constructor. + * + * @param name + * the name of the font + */ + private Font(String name) { + this(name, 0); + } + + /** + * Cstr + * + * @param name + * the name of the font + * @param modifiers + * the modifiers value + */ + private Font(String name, int modifiers) { + this.name = name; + this.modifiers = modifiers; + } + + /* + * Static methods + */ + + /** + * Return the Font for a given Scilab index + * + * @param index + * the index + * @return the associated font + */ + public static Font getFont(int index) { + return Font.values()[index % Font.values().length]; + } + + /** + * Map a Scilab size to a Java font size. + * + * @param scilabSize + * the size of the font (using getfont) + * @return the corresponding java size + */ + // CSOFF: MagicNumber + public static int getSize(int scilabSize) { + final int ret; + if (scilabSize <= 0) { + ret = 0; + } else if (scilabSize < 1) { + ret = (13 * scilabSize); + } else if (scilabSize > 4) { + ret = (7 * (scilabSize - 4) + 17); + } else { + ret = (int) (13 + 1.2 * (scilabSize - 1)); + } + return ret; + } + + // CSON: MagicNumber + + /** + * Map a Scilab color to a Java color. + * + * @param scilabColor + * the color of the font (using getcolor) + * @return the corresponding java color + */ + // CSOFF: MagicNumber + public static Color getColor(int scilabColor) { + return COLORMAP[scilabColor % COLORMAP.length]; + } + + // CSON: MagicNumber + + /** + * Map a Scilab size to a Java font size. + * + * @param scilabSize + * the size of the font (using getfont) + * @return the corresponding java size + */ + public static int getSize(String scilabSize) { + return getSize(Integer.parseInt(scilabSize)); + } + + /** + * Map a Scilab color to a Java color. + * + * @param scilabColor + * the color of the font (using getcolor) + * @return the corresponding java color + */ + public static Color getColor(String scilabColor) { + return getColor(Integer.parseInt(scilabColor)); + } + + /* + * Accessors + */ + + /** + * @return the name of the font + */ + public String getName() { + return name; + } + + /** + * @return the modifiers + */ + public int getModifiers() { + return modifiers; + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/utils/MathMLRenderUtils.java b/modules/graph/src/java/org/scilab/modules/graph/utils/MathMLRenderUtils.java new file mode 100755 index 000000000..2a3d7c491 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/utils/MathMLRenderUtils.java @@ -0,0 +1,81 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.utils; + +import java.awt.Component; +import java.io.IOException; +import java.io.StringReader; +import java.util.Map; +import java.util.WeakHashMap; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import net.sourceforge.jeuclid.swing.JMathComponent; + +import org.scilab.modules.graph.view.SupportedLabelType; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * Contains useful method for rendering a MathML formula. + * + * Before accessing this class you must load the jeuclid.jar file on the + * classpath. + */ +public final class MathMLRenderUtils { + /** + * Cache for the generated MathML components + */ + private static Map<String, JMathComponent> generatedMathMLComponents = new WeakHashMap<String, JMathComponent>(); + + /** + * Return a cached or a new instance of a JMathComponent generated from + * text. + * @param text the MathML formula + * @return the {@link JMathComponent} instance + * @throws SAXException when the text is not a valid XML file + */ + public static Component getMathMLComponent(String text) throws SAXException { + String escapedText = SupportedLabelType.MathML.escape(text); + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder; + Document doc; + JMathComponent comp; + + comp = generatedMathMLComponents.get(text); + if (comp == null) { + try { + builder = factory.newDocumentBuilder(); + doc = builder.parse(new InputSource(new StringReader(escapedText))); + comp = new JMathComponent(); + comp.setDocument(doc.getFirstChild()); + generatedMathMLComponents.put(text, comp); + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + return comp; + } + + /** + * This class is a static singleton + */ + private MathMLRenderUtils() { } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/utils/ScilabExported.java b/modules/graph/src/java/org/scilab/modules/graph/utils/ScilabExported.java new file mode 100755 index 000000000..45eea06e9 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/utils/ScilabExported.java @@ -0,0 +1,34 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.utils; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * Annotation used to specify that this method is exported to JNI via giws. + */ +@Target( { ElementType.METHOD, ElementType.CONSTRUCTOR }) +public @interface ScilabExported { + /** + * The module where the giws file and generated implementations are + * presents, eg "xcos" for SCI/modules/xcos/src/jni/Xcos.giws.xml . + */ + String module(); + + /** + * Giws file name, eg "Xcos.giws.xml" for + * SCI/modules/xcos/src/jni/Xcos.giws.xml . + */ + String filename(); +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/utils/ScilabGraphConstants.java b/modules/graph/src/java/org/scilab/modules/graph/utils/ScilabGraphConstants.java new file mode 100755 index 000000000..7c48b9eab --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/utils/ScilabGraphConstants.java @@ -0,0 +1,78 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.utils; + +import com.mxgraph.util.mxConstants; + +/** + * Define all the constants used on a Scilab graph + */ +public class ScilabGraphConstants extends mxConstants { + + /** + * Defines the size of the arcs for rounded edges. Default is 10. + */ + public static final double LINE_ARCSIZE = 200; + + /** + * Defines the key for the centerArrow style. + */ + public static final String STYLE_CENTERARROW = "centerArrow"; + + /** + * Defines the key for the centerSize style. The type of this value is + * <code>float</code> and the value represents the size of the center + * marker in pixels. + */ + public static final String STYLE_CENTERSIZE = "centerSize"; + + /** + * Defines the value for spline edge shape + */ + public static final String SHAPE_SPLINE = "spline"; + /** + * Defines the value if we want the arrow to be centered + */ + public static final String ARROW_POSITION_CENTER = "center"; + + /** + * Defines the key for flip image . + */ + public static final String STYLE_FLIP = "flip"; + + /** + * Defines the key for mirror image . + */ + public static final String STYLE_MIRROR = "mirror"; + + /* HTML */ + /** + * The html begin symbol + */ + public static final String HTML_BEGIN = "<html>"; + /** + * The html end symbol + */ + public static final String HTML_END = "</html>"; + /** + * The html new line symbol + */ + public static final String HTML_NEWLINE = "<br>"; + + /* Events */ + /** Name of the edit event */ + public static final String EVENT_CHANGE_EDIT = "edit"; + + /** This class is a static singleton, thus it must not be instantiated */ + protected ScilabGraphConstants() { } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/utils/ScilabGraphMessages.java b/modules/graph/src/java/org/scilab/modules/graph/utils/ScilabGraphMessages.java new file mode 100755 index 000000000..c21f94ca1 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/utils/ScilabGraphMessages.java @@ -0,0 +1,40 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Vincent COUVERT + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ +package org.scilab.modules.graph.utils; + +import org.scilab.modules.localization.Messages; + +/** + * Internationalized messages for this graph + */ +public final class ScilabGraphMessages { + + public static final String COPY = Messages.gettext("Copy"); + public static final String CUT = Messages.gettext("Cut"); + public static final String GROUP = Messages.gettext("Group"); + public static final String UNGROUP = Messages.gettext("UnGroup"); + public static final String PASTE = Messages.gettext("Paste"); + public static final String REDO = Messages.gettext("Redo"); + public static final String UNDO = Messages.gettext("Undo"); + public static final String ZOOM_IN = Messages.gettext("Zoom In"); + public static final String ZOOM_OUT = Messages.gettext("Zoom Out"); + public static final String DELETE = Messages.gettext("Delete"); + public static final String SELECT_ALL = Messages.gettext("Select all"); + public static final String INVERT_SELECTION = Messages.gettext("Invert selection"); + public static final String UNTITLED = Messages.gettext("Untitled - %s"); + + /** This class is a static singleton, thus it must not be instantiated */ + private ScilabGraphMessages() { + } + +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/utils/ScilabGraphRenderer.java b/modules/graph/src/java/org/scilab/modules/graph/utils/ScilabGraphRenderer.java new file mode 100755 index 000000000..40bedccc3 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/utils/ScilabGraphRenderer.java @@ -0,0 +1,131 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.utils; + +import java.awt.Color; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; + +import org.apache.batik.dom.GenericDOMImplementation; +import org.apache.batik.svggen.CachedImageHandlerBase64Encoder; +import org.apache.batik.svggen.GenericImageHandler; +import org.apache.batik.svggen.SVGGeneratorContext; +import org.apache.batik.svggen.SVGGraphics2D; +import org.apache.batik.svggen.SVGGraphics2DIOException; +import org.scilab.modules.graph.ScilabCanvas; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; + +import com.mxgraph.canvas.mxGraphics2DCanvas; +import com.mxgraph.canvas.mxICanvas; +import com.mxgraph.util.mxCellRenderer; +import com.mxgraph.util.mxRectangle; +import com.mxgraph.util.mxCellRenderer.CanvasFactory; +import com.mxgraph.view.mxGraph; + +/** + * This class implement Export operation by rendering graphs. + * + * Implement custom rendering as {@link mxCellRenderer} does. + */ +public class ScilabGraphRenderer { + + /** + * This class is a static singleton + */ + private ScilabGraphRenderer() { + } + + /** + * Create an SVG Document containing a rendered graph. + * + * Use the Batik svg-generator engine to perform rendering. + * + * @param graph + * The graph to render + * @param cells + * the cells to render + * @param scale + * the scale + * @param background + * the current background + * @param clip + * the current clip + * @param filename + * the file where to stream. + */ + public static void createSvgDocument(mxGraph graph, Object[] cells, + double scale, Color background, mxRectangle clip, String filename) { + + // Get a DOMImplementation. + DOMImplementation domImpl = GenericDOMImplementation + .getDOMImplementation(); + + // Create an instance of org.w3c.dom.Document. + String svgNS = "http://www.w3.org/2000/svg"; + Document doc = domImpl.createDocument(svgNS, "svg", null); + + // Adding comments and customs + SVGGeneratorContext ctx = SVGGeneratorContext.createDefault(doc); + ctx.setComment("Generated with Batik SVG Generator"); + ctx.setEmbeddedFontsOn(true); + GenericImageHandler ihandler = new CachedImageHandlerBase64Encoder(); + ctx.setGenericImageHandler(ihandler); + + // Create an instance of the SVG Generator. + final SVGGraphics2D svgGenerator = new SVGGraphics2D(ctx, true); + + // Render the graph using the SVGGraphics2D object + mxCellRenderer.drawCells(graph, cells, scale, clip, + new CanvasFactory() { + private mxGraphics2DCanvas canvas; + + /** + * Create a new canvas for drawing + * + * @param width + * the canvas width + * @param height + * the canvas height + * @return a new canvas used for drawing + */ + @Override + public mxICanvas createCanvas(int width, int height) { + if (canvas == null) { + canvas = new ScilabCanvas(); + canvas.setGraphics(svgGenerator); + } + + return canvas; + } + }); + + // Finally, stream out SVG to the standard output using + // UTF-8 encoding. + boolean useCSS = true; // we want to use CSS style attributes + Writer out; + try { + out = new OutputStreamWriter(new FileOutputStream(filename), "UTF-8"); + svgGenerator.stream(out, useCSS); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } catch (SVGGraphics2DIOException e) { + e.printStackTrace(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/utils/ScilabGraphUtils.java b/modules/graph/src/java/org/scilab/modules/graph/utils/ScilabGraphUtils.java new file mode 100755 index 000000000..ddf6d1bce --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/utils/ScilabGraphUtils.java @@ -0,0 +1,230 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.utils; + +import java.awt.geom.Dimension2D; +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.logging.Logger; + +import javax.swing.Icon; + +import org.apache.batik.bridge.BridgeContext; +import org.apache.batik.bridge.DocumentLoader; +import org.apache.batik.bridge.GVTBuilder; +import org.apache.batik.bridge.UserAgent; +import org.apache.batik.bridge.UserAgentAdapter; +import org.apache.batik.dom.svg.SAXSVGDocumentFactory; +import org.apache.batik.gvt.GraphicsNode; +import org.apache.batik.util.XMLResourceDescriptor; +import org.scilab.forge.jlatexmath.ParseException; +import org.scilab.forge.jlatexmath.TeXConstants; +import org.scilab.forge.jlatexmath.TeXFormula; +import org.scilab.forge.jlatexmath.TeXIcon; +import org.scilab.modules.graph.view.SupportedLabelType; +import org.w3c.dom.Document; + +import com.mxgraph.util.mxUtils; + +/** + * Utilities functions for ScilabGraph + */ +public final class ScilabGraphUtils extends mxUtils { + /** + * Cache for the generated SVG components + */ + private static Map<URL, WeakReference<GraphicsNode>> generatedSVGComponents = new HashMap<URL, WeakReference<GraphicsNode>>(); + /** + * Cache for the generated SVG document sizes + */ + private static Map<URL, Dimension2D> generatedSVGSizes = new HashMap<URL, Dimension2D>(); + + /** + * Cache for the generated latex icons + */ + private static Map<Float, Map<String, TeXIcon>> generatedLatexIcons = new WeakHashMap<Float, Map<String, TeXIcon>>(); + + /** + * Table conversion between escaped/unescaped HTML symbols + */ + private static final String[][] HTML_ESCAPE_TABLE = { { "<", "<" }, { ">", ">" }, { "&", "&" }, { """, "\"" }, { "à", "\u00e0" }, + { "À", "\u00c0" }, { "â", "\u00e2" }, { "ä", "\u00e4" }, { "Ä", "\u00c4" }, { "Â", "\u00c2" }, + { "å", "\u00e5" }, { "Å", "\u00c5" }, { "æ", "\u00e6" }, { "Æ", "\u00c6" }, { "ç", "\u00e7" }, + { "Ç", "\u00c7" }, { "é", "\u00e9" }, { "É", "\u00c9" }, { "è", "\u00e8" }, { "È", "\u00c8" }, + { "ê", "\u00ea" }, { "Ê", "\u00ca" }, { "ë", "\u00eb" }, { "Ë", "\u00cb" }, { "ï", "\u00ef" }, { "Ï", "\u00cf" }, + { "ô", "\u00f4" }, { "Ô", "\u00d4" }, { "ö", "\u00f6" }, { "Ö", "\u00d6" }, { "ø", "\u00f8" }, + { "Ø", "\u00d8" }, { "ß", "\u00df" }, { "ù", "\u00f9" }, { "Ù", "\u00d9" }, { "û", "\u00fb" }, + { "Û", "\u00db" }, { "ü", "\u00fc" }, { "Ü", "\u00dc" }, { " ", " " }, { "®", "\u00a9" }, { "©", "\u00ae" }, + { "€", "\u20a0" } + }; + + /** + * Return a cached or a new instance of a {@link GraphicsNode} generated + * from the SVG file. + * + * @param filename + * the file to parse + * @return the corresponding graphic node + */ + public static GraphicsNode getSVGComponent(URL filename) { + WeakReference<GraphicsNode> nodeRef; + GraphicsNode node; + + nodeRef = generatedSVGComponents.get(filename); + if (nodeRef != null) { + node = nodeRef.get(); + } else { + node = null; + } + + if (node == null) { + try { + String xmlParser = XMLResourceDescriptor.getXMLParserClassName(); + SAXSVGDocumentFactory df = new SAXSVGDocumentFactory(xmlParser); + Document doc = df.createDocument(filename.toString()); + UserAgent userAgent = new UserAgentAdapter(); + DocumentLoader loader = new DocumentLoader(userAgent); + BridgeContext ctx = new BridgeContext(userAgent, loader); + ctx.setDynamicState(BridgeContext.STATIC); + GVTBuilder builder = new GVTBuilder(); + + node = builder.build(ctx, doc); + + generatedSVGComponents.put(filename, node.getWeakReference()); + generatedSVGSizes.put(filename, ctx.getDocumentSize()); + } catch (IOException e) { + Logger.getLogger(ScilabGraphUtils.class.getName()).severe(e.toString()); + } + } + return node; + } + + /** + * Get the document size for a given URL. + * + * This method use the Document size cache to get the svg element dimension + * and not the real size of the graphical tree. + * + * @param filename + * the file + * @return the dimension of the file + */ + public static Dimension2D getSVGDocumentSizes(URL filename) { + Dimension2D ret = generatedSVGSizes.get(filename); + + // Generate the GraphicsNode if not available + if (ret == null) { + getSVGComponent(filename); + ret = generatedSVGSizes.get(filename); + } + return ret; + } + + /** + * Return a cached or a new instance of a TexIcon generated from the text. + * + * @param text + * the latex formula + * @return the TeXIcon + * @throws ParseException + * when the text is not a valid formula + */ + public static Icon getTexIcon(String text, float size) throws ParseException { + TeXIcon icon; + String escapedText = SupportedLabelType.Latex.escape(text); + + Map<String, TeXIcon> iconMap = generatedLatexIcons.get(size); + if (iconMap == null) { + iconMap = new WeakHashMap<String, TeXIcon>(); + generatedLatexIcons.put(size, iconMap); + } + + icon = iconMap.get(escapedText); + if (icon == null) { + TeXFormula tex = new TeXFormula(escapedText); + icon = tex.createTeXIcon(TeXConstants.STYLE_DISPLAY, size); + iconMap.put(escapedText, icon); + } + return icon; + } + + /** + * Remove the blank char on the beginning of the sequence. This method + * modify the {@link StringBuilder} in place. + * + * @param seq + * the sequence + */ + public static void removeBlanks(StringBuilder seq) { + int i; + for (i = 0; i < seq.length(); i++) { + char car = seq.charAt(i); + if (car != ' ' && car != '\n') { + break; + } + } + + seq.delete(0, i); + } + + /** + * Unescape html + * + * This method modify the {@link StringBuilder} in place. + * + * @param escapedText + * the working text + * @param index + * the beginning index + */ + public static void unescape(StringBuilder escapedText, int index) { + int start, end, table_index; + + start = escapedText.indexOf("&", index); + if (start > -1) { + end = escapedText.indexOf(";", start); + // we don't start from the beginning + // the next time, to handle the case of + // the & + index = start + 1; + + if (end > start) { + String temp = escapedText.substring(start, end + 1); + // search in HTML_ESCAPE_TABLE[][] if temp is there + table_index = 0; + while (table_index < HTML_ESCAPE_TABLE.length) { + if (HTML_ESCAPE_TABLE[table_index][0].equals(temp)) { + break; + } else { + table_index++; + } + } + if (table_index < HTML_ESCAPE_TABLE.length) { + escapedText.replace(start, end + 1, HTML_ESCAPE_TABLE[table_index][1]); + unescape(escapedText, index); // recursive call + } + } + } + return; + } + + /** + * This class is a static singleton thus it must not be instantiated + */ + private ScilabGraphUtils() { + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/utils/StyleMap.java b/modules/graph/src/java/org/scilab/modules/graph/utils/StyleMap.java new file mode 100755 index 000000000..11ee643b7 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/utils/StyleMap.java @@ -0,0 +1,85 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2009 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.utils; + +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map.Entry; + +/** + * Perform useful conversions between a style string and key/value based map. + */ +@SuppressWarnings(value = { "serial" }) +public final class StyleMap extends LinkedHashMap<String, String> { + + /** + * Create a Map from a style string + * @param style The string which contains key=value list + */ + public StyleMap(String style) { + super(); + putAll(style); + } + + /** + * Put all the style attributes to the current map + * @param style The string which contains key=value list + * @return the current map + */ + public StyleMap putAll(String style) { + if (style != null && style.length() > 0) { + final String[] pairs = style.split(";"); + + for (String keyValue : pairs) { + final int sep = keyValue.indexOf('='); + + if (sep >= 0) { + put(keyValue.substring(0, sep), keyValue.substring(sep + 1)); + } else { + put(keyValue, null); + } + } + } + return this; + } + + /** + * Export to a key=value; string + * @return formatted string + */ + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + String valueRef = null; + + for (Iterator<Entry<String, String>> iterator = entrySet().iterator(); + iterator.hasNext();) { + Entry<String, String> entry = iterator.next(); + + str.append(entry.getKey()); + + valueRef = entry.getValue(); + if (valueRef != null && valueRef.length() > 0) { + str.append("="); + str.append(valueRef); + } + + if (iterator.hasNext()) { + str.append(";"); + } + + } + + return str.toString(); + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/view/ScilabGraphView.java b/modules/graph/src/java/org/scilab/modules/graph/view/ScilabGraphView.java new file mode 100755 index 000000000..6972ba790 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/view/ScilabGraphView.java @@ -0,0 +1,169 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.view; + +import java.awt.Component; +import java.util.Map; + +import javax.swing.Icon; + +import org.scilab.modules.graph.utils.MathMLRenderUtils; +import org.scilab.modules.graph.utils.ScilabGraphUtils; +import org.scilab.modules.gui.messagebox.ScilabModalDialog; +import org.xml.sax.SAXException; + +import com.mxgraph.model.mxCell; +import com.mxgraph.util.mxConstants; +import com.mxgraph.util.mxPoint; +import com.mxgraph.util.mxRectangle; +import com.mxgraph.util.mxUtils; +import com.mxgraph.view.mxCellState; +import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxGraphView; + +/** + * Implement specific method to render a graph + */ +public class ScilabGraphView extends mxGraphView { + + /** + * Default constructor + * @param graph the associated graph + */ + public ScilabGraphView(mxGraph graph) { + super(graph); + } + + /** + * Updates the label bounds in the given state. + * @param state the cell visible state + */ + @Override + public void updateLabelBounds(mxCellState state) { + Object cell = state.getCell(); + Map<String, Object> style = state.getStyle(); + String label = graph.getLabel(cell); + + SupportedLabelType type = SupportedLabelType.getFromHTML(label); + mxRectangle labelBounds; + double w; + double h; + + switch (type) { + case Latex: + /* + * As we render text as an image, the label bounds are set to the + * scaled generated image values. + */ + try { + float fontSize = (float) (mxUtils.getInt(style, mxConstants.STYLE_FONTSIZE, 16) * scale); + + final Icon icon = ScilabGraphUtils.getTexIcon(label, fontSize); + // the icon is generated scaled so width and height are already scaled + w = icon.getIconWidth(); + h = icon.getIconHeight(); + + final mxPoint offset = state.getAbsoluteOffset(); + double x = offset.getX(); + double y = offset.getY(); + + final mxRectangle vertexBounds; + if (!graph.getModel().isEdge(cell)) { + vertexBounds = state; + + x += vertexBounds.getX(); + y += vertexBounds.getY();; + + // the label is always centered + x -= (w - vertexBounds.getWidth()) / 2 ; + y -= (h - vertexBounds.getHeight()) / 2 ; + } + + labelBounds = new mxRectangle(x, y, w, h); + } catch (Exception e) { + // popup an error + // FIXME: use a ScilabGraphTab instead of null there + ScilabModalDialog.show(null, e.getLocalizedMessage()); + + // Set a non-rendering label on the model + // label will be printed (state contains the value) + mxCell c = (mxCell) cell; + c.setValue(((String) c.getValue()).substring(1)); + labelBounds = getDefaultBounds(state, cell, style, label); + } + break; + + case MathML: + /* + * As we render text as an image, the label bounds are set to the + * scaled generated image values. + */ + try { + Component comp = MathMLRenderUtils.getMathMLComponent(label); + w = comp.getWidth(); + h = comp.getHeight(); + + final mxPoint offset = state.getOrigin(); + final mxRectangle size = new mxRectangle(); + size.setWidth(w); + size.setHeight(h); + + labelBounds = mxUtils.getScaledLabelBounds(offset.getX(), + offset.getY(), size, state.getWidth(), + state.getHeight(), style, scale); + } catch (SAXException e) { + // popup an error + // FIXME: use a ScilabGraphTab instead of null there + ScilabModalDialog.show(null, e.getLocalizedMessage()); + + // Set a non-rendering label on the model + // label will be printed (state contains the value) + mxCell c = (mxCell) cell; + c.setValue(((String) c.getValue()).substring(1)); + labelBounds = getDefaultBounds(state, cell, style, label); + } + break; + + default: + labelBounds = getDefaultBounds(state, cell, style, label); + break; + } + + state.setLabelBounds(labelBounds); + } + + /** + * Return the default bounds calculated as if the label were text. + * @param state the cell state + * @param cell the current cell + * @param style the cell style + * @param label the current text + * @return the calculated bounds + */ + private mxRectangle getDefaultBounds(mxCellState state, Object cell, + Map<String, Object> style, String label) { + mxRectangle labelBounds; + mxRectangle vertexBounds; + + if (!graph.getModel().isEdge(cell)) { + vertexBounds = state; + } else { + vertexBounds = null; + } + + labelBounds = mxUtils.getLabelPaintBounds(label, style, graph + .isHtmlLabel(cell), state.getAbsoluteOffset(), vertexBounds, + scale); + return labelBounds; + } +} diff --git a/modules/graph/src/java/org/scilab/modules/graph/view/SupportedLabelType.java b/modules/graph/src/java/org/scilab/modules/graph/view/SupportedLabelType.java new file mode 100755 index 000000000..cccd7a318 --- /dev/null +++ b/modules/graph/src/java/org/scilab/modules/graph/view/SupportedLabelType.java @@ -0,0 +1,155 @@ +/* + * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab + * Copyright (C) 2010 - DIGITEO - Clement DAVID + * + * This file must be used under the terms of the CeCILL. + * This source file is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at + * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt + * + */ + +package org.scilab.modules.graph.view; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.scilab.modules.graph.utils.ScilabGraphUtils; +import org.scilab.modules.jvm.LoadClassPath; + +import com.mxgraph.util.mxUtils; + +/** + * Specify the supported label type + */ +public enum SupportedLabelType { + /** The label is HTML formatted. This is the default */ + HTML, + /** The label is Latex formatted */ + Latex, + /** The label is MathML formatted */ + MathML; + + /** + * Pattern to match the <br> HTML tag + */ + private static final Pattern BR_PATTERN = Pattern.compile("<br>\\p{Blank}*"); + + /** The '$' symbol is used to tag a LaTeX expression */ + private static final char LATEX_TAG = '$'; + /** The '^' symbol is used to tag a LaTeX expression */ + private static final char MATHML_TAG = '^'; + /** The id used on classpath.xml to load MathML JARs */ + private static final String CLASSPATH_MATHML_NAME = "xcos_mathml_rendering"; + + /** + * Get the {@link SupportedLabelType} for the label. + * + * The type of the label is found with a specific first character. + * <ul> + * <li>'$' : for {@value SupportedLabelType#Latex} markup</li> + * <li>'^' : for {@value SupportedLabelType#MathML} markup</li> + * </ul> + * + * The {@value SupportedLabelType#HTML} is used as default. + * + * @param text The label to parse + * @return the type of the label + */ + public static SupportedLabelType getFromText(String text) { + if (text.length() > 0) { + if ((text.charAt(0) == LATEX_TAG) && ( + text.charAt(text.length() - 1) == LATEX_TAG)) { + return Latex; + } else if ((text.charAt(0) == MATHML_TAG) && ( + text.charAt(text.length() - 1) == MATHML_TAG)) { + LoadClassPath.loadOnUse(CLASSPATH_MATHML_NAME); + return MathML; + } + } + return HTML; + } + + /** + * Get the {@link SupportedLabelType} for the HTML formatted string. + * + * The type of the label is found with a specific first character of the + * content. The HTML markup is escaped automatically. + * <ul> + * <li>'$' : for {@value SupportedLabelType#Latex} markup</li> + * <li>'^' : for {@value SupportedLabelType#MathML} markup</li> + * </ul> + * + * The {@value SupportedLabelType#HTML} is used as default. + * + * @param html The HTML string to parse + * @return the type of the label + */ + public static SupportedLabelType getFromHTML(String html) { + if (html.length() > 0 && html.charAt(0) == '<') { + + final StringBuilder content = new StringBuilder(mxUtils + .getBodyMarkup(html, false)); + + ScilabGraphUtils.removeBlanks(content); + + if ((content.length() > 0) && (content.charAt(0) == LATEX_TAG) + && (content.charAt(content.length() - 1) == LATEX_TAG)) { + return Latex; + } else if ((content.length() > 0) + && (content.charAt(0) == MATHML_TAG) + && (content.charAt(content.length() - 1) == MATHML_TAG)) { + LoadClassPath.loadOnUse(CLASSPATH_MATHML_NAME); + return MathML; + } + } else { + return getFromText(html); + } + + return HTML; + } + + /** + * Escape the text for the current type. + * @param text the text to escape + * @return the escaped text + */ + public String escape(String text) { + StringBuilder escapedText; + + if (text.startsWith("<html")) { + escapedText = new StringBuilder(ScilabGraphUtils.getBodyMarkup(text, true)); + } else { + escapedText = new StringBuilder(text); + } + + switch (this) { + case MathML: + case Latex: + // Unescape content + ScilabGraphUtils.unescape(escapedText, 0); + + // Removing <br> + Matcher m = BR_PATTERN.matcher(escapedText); + while (m.find()) { + escapedText.replace(m.start(), m.end(), ""); + m.reset(); + } + + // Removing blank + ScilabGraphUtils.removeBlanks(escapedText); + + // Removing the first and last char (tag) + escapedText.delete(0, 1); + int length = escapedText.length(); + escapedText.delete(length - 1, length); + break; + + default: + break; + } + + return escapedText.toString(); + } +} |