Updated and refactored SVG visuaizations
diff --git a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/AbstractConfig.java b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/AbstractConfig.java new file mode 100644 index 0000000..ed5d349 --- /dev/null +++ b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/AbstractConfig.java
@@ -0,0 +1,79 @@ +/** + ******************************************************************************** + * Copyright (c) 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial API and implementation + ******************************************************************************** + */ + +package org.eclipse.app4mc.amalthea.visualizations.svg; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +import org.eclipse.app4mc.visualization.ui.VisualizationParameters; + +public abstract class AbstractConfig { + + private static final String SCALE_KEY = "Scale"; + private static final String SCALE_DEFAULT = "100"; + + private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this); + + protected final VisualizationParameters parameters; + + protected AbstractConfig(VisualizationParameters viewParameters) { + parameters = viewParameters; + } + + public int getScale() { + return Integer.parseInt(parameters.getOrDefault(SCALE_KEY, SCALE_DEFAULT)); + } + + /** + * Sets a new scale value (if the new value is different and within the bounds [10, 200]) + * + * @param newScale + * @return true if value was changed + */ + public boolean setScale(int newScale) { + int oldScale = getScale(); + if (oldScale == newScale || newScale < 10 || newScale > 200) { + return false; + } + + parameters.put(SCALE_KEY, Integer.toString(newScale)); + firePropertyChange("scale", oldScale, newScale); + return true; + } + + public boolean decrementScale() { + return setScale(Math.max(10, getScale() - 10)); // minimum 10 % + } + + public boolean incrementScale() { + return setScale(Math.min(200, getScale() + 10)); // maximum 200 % + } + + // property change handling + + public void addChangeListener(PropertyChangeListener listener) { + changeSupport.addPropertyChangeListener(listener); + } + + public void removeChangeListener(PropertyChangeListener listener) { + changeSupport.removePropertyChangeListener(listener); + } + + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + changeSupport.firePropertyChange(propertyName, oldValue, newValue); + } + +}
diff --git a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/AbstractVisualization.java b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/AbstractVisualization.java new file mode 100644 index 0000000..b23df1b --- /dev/null +++ b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/AbstractVisualization.java
@@ -0,0 +1,157 @@ +/** + ******************************************************************************** + * Copyright (c) 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial API and implementation + ******************************************************************************** + */ + +package org.eclipse.app4mc.amalthea.visualizations.svg; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.function.Consumer; + +import org.eclipse.app4mc.visualization.util.svg.SvgUtil; +import org.eclipse.core.runtime.Platform; +import org.eclipse.e4.core.services.events.IEventBroker; +import org.eclipse.jface.layout.RowLayoutFactory; +import org.eclipse.swt.SWT; +import org.eclipse.swt.browser.Browser; +import org.eclipse.swt.browser.LocationListener; +import org.eclipse.swt.custom.CLabel; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; + +public abstract class AbstractVisualization { + + /** + * Helper for adding a toggle button + * + * @param parent container element + * @param text label text + * @param toolTip label tool tip + * @param f button select action; takes the button's selection status as an argument + * @param initialSelected initial selection state of the button + */ + protected void addToggleButton(Composite parent, String text, String toolTip, final Consumer<Boolean> f, boolean initialSelected) { + final Button btn = new Button(parent, SWT.TOGGLE | SWT.FLAT); + btn.setText(text); + btn.setToolTipText(toolTip); + btn.setSelection(initialSelected); + btn.addListener(SWT.Selection, e -> f.accept(btn.getSelection())); + } + + protected void addZoomBox(Composite buttonArea, AbstractConfig config) { + // Align the box with the other buttons + final Composite zoomArea = new Composite(buttonArea, SWT.NONE); + RowLayoutFactory.fillDefaults().margins(1, 1).applyTo(zoomArea); + + final Composite box = new Composite(zoomArea, SWT.BORDER); + + final Button btnLeft = new Button(box, SWT.ARROW | SWT.LEFT | SWT.FLAT); + btnLeft.addListener(SWT.Selection, e -> config.decrementScale()); + + final CLabel scaleLabel = new CLabel(box, SWT.FLAT | SWT.CENTER); + scaleLabel.setText(String.format("%d %%", config.getScale())); // set initial label text + + config.addChangeListener(e -> { + if (e.getPropertyName().equals("scale")) + scaleLabel.setText(String.format("%d %%", (int) e.getNewValue())); // update label text + }); + + final Button btnRight = new Button(box, SWT.ARROW | SWT.RIGHT | SWT.FLAT); + btnRight.addListener(SWT.Selection, e -> config.incrementScale()); + + RowLayoutFactory.fillDefaults().fill(true).applyTo(box); + } + + protected Browser addBrowser(Composite pane, IEventBroker broker, final Context context) { + Browser browser = new Browser(pane, SWT.NONE); + + // Setup navigation to a selected element in the model viewer + if (broker != null) { + browser.addLocationListener(LocationListener.changingAdapter(c -> { + c.doit = true; + + Object target = null; + int idx = c.location.lastIndexOf('#'); + if (idx >= 0) { + target = context.diagram.getObjectById(c.location.substring(idx + 1)); + } + if (target != null) { + HashMap<String, Object> data = new HashMap<>(); + data.put("modelElements", Collections.singletonList(target)); + broker.send("org/eclipse/app4mc/amalthea/editor/SELECT", data); + + c.doit = false; + } + })); + } + + // React to configuration parameter changes + context.config.addChangeListener(e -> { + if (e.getPropertyName().equals("scale")) + updateSvgScale(browser, (int) e.getNewValue()); + if (e.getPropertyName().startsWith("parameter")) + updateBrowserContent(browser, context); + }); + + return browser; + } + + protected void updateSvgScale(Browser browser, int newScale) { + // Update SVG size in browser via JavaScript/DOM + if (browser != null) { + browser.execute(SvgUtil.buildUpdateScaleCommand(newScale)); + } + } + + /** + * Visualizes a given model element in a browser. + * <p> + * The plantUML graph is constructed and compiled in a separate thread. + * + * @param browser + * @param context + */ + protected void updateBrowserContent(Browser browser, Context context) { + new Thread(() -> { + // Build PlantUML diagram text + updateDiagram(context); + + // Render to SVG + String result; + try { + result = context.diagram.renderToSvg(); + } catch (IOException e) { + result = "Error invoking PlantUML: \"" + e.getMessage() + + "\". Make sure you have configured the path to the dot executable properly in the PlantUML preferences."; + Platform.getLog(EventChainMapVisualization.class).error(result, e); + return; + } + + // Apply initial scale and display + if (result != null && !browser.isDisposed()) { + final String browserContent = SvgUtil.initiallyApplyScale(result, context.config.getScale()); + browser.getDisplay().asyncExec(() -> { + if (!browser.isDisposed()) { + browser.setText(browserContent); + } + }); + } + + }).start(); + } + + protected abstract void updateDiagram(Context context); + +}
diff --git a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/Context.java b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/Context.java new file mode 100644 index 0000000..7cfb75f --- /dev/null +++ b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/Context.java
@@ -0,0 +1,33 @@ +/** + ******************************************************************************** + * Copyright (c) 2023 Robert Bosch GmbH. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Robert Bosch GmbH - initial API and implementation + ******************************************************************************** + */ + +package org.eclipse.app4mc.amalthea.visualizations.svg; + +import org.eclipse.app4mc.visualization.util.svg.AbstractDiagram; +import org.eclipse.emf.ecore.EObject; + +public final class Context { + public final EObject object; + public final AbstractConfig config; + public final AbstractDiagram diagram; + + public Context(EObject object, AbstractConfig config, AbstractDiagram diagram) { + super(); + this.object = object; + this.config = config; + this.diagram = diagram; + } + +}
diff --git a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapConfig.java b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapConfig.java index 80a83c6..42c0d5c 100644 --- a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapConfig.java +++ b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapConfig.java
@@ -15,33 +15,27 @@ package org.eclipse.app4mc.amalthea.visualizations.svg; -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; - import org.eclipse.app4mc.visualization.ui.VisualizationParameters; /** * Configuration of the visualization * */ -public class EventChainMapConfig { +public class EventChainMapConfig extends AbstractConfig { private static final String SHOW_ALL_EVENTS_KEY = "ShowAllEvents"; private static final String SHOW_ALL_EVENTS_DEFAULT = "true"; + private static final String SHOW_LINKS_KEY = "ShowLinks"; + private static final String SHOW_LINKS_DEFAULT = "true"; + private static final String EXPAND_SUBCHAIN_REFERENCES_KEY = "ExpandSubchainReferences"; private static final String EXPAND_SUBCHAIN_REFERENCES_DEFAULT = "false"; - + private static final String SHOW_REPEATING_EVENTS_GRAYED_KEY = "ShowRepeatingEventsGrayed"; private static final String SHOW_REPEATING_EVENTS_GRAYED_DEFAULT = "false"; - - private static final String SCALE_KEY = "Scale"; - private static final String SCALE_DEFAULT = "100"; - - private final VisualizationParameters parameters; - private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this); - + public EventChainMapConfig(VisualizationParameters viewParameters) { - parameters = viewParameters; + super(viewParameters); } public boolean isShowAllEvents() { @@ -53,13 +47,13 @@ firePropertyChange("parameter1", null, showAllEvents); } - public boolean isShowRepeatingEventsGrayed() { - return Boolean.parseBoolean(parameters.getOrDefault(SHOW_REPEATING_EVENTS_GRAYED_KEY, SHOW_REPEATING_EVENTS_GRAYED_DEFAULT)); + public boolean isShowLinks() { + return Boolean.parseBoolean(parameters.getOrDefault(SHOW_LINKS_KEY, SHOW_LINKS_DEFAULT)); } - public void setShowRepeatingEventsGrayed(boolean showEventsGrayed) { - parameters.put(SHOW_REPEATING_EVENTS_GRAYED_KEY, Boolean.toString(showEventsGrayed)); - firePropertyChange("parameter2", null, showEventsGrayed); + public void setShowLinks(boolean showLinks) { + parameters.put(SHOW_LINKS_KEY, Boolean.toString(showLinks)); + firePropertyChange("parameter2", null, showLinks); } public boolean isExpandSubchainReferences() { @@ -71,47 +65,13 @@ firePropertyChange("parameter3", null, expandReferences); } - public int getScale() { - return Integer.parseInt(parameters.getOrDefault(SCALE_KEY, SCALE_DEFAULT)); + public boolean isShowRepeatingEventsGrayed() { + return Boolean.parseBoolean(parameters.getOrDefault(SHOW_REPEATING_EVENTS_GRAYED_KEY, SHOW_REPEATING_EVENTS_GRAYED_DEFAULT)); } - /** - * Sets a new scale value (if the new value is different and within the bounds [10, 200]) - * - * @param newScale - * @return true if value was changed - */ - public boolean setScale(int newScale) { - int oldScale = getScale(); - if (oldScale == newScale || newScale < 10 || newScale > 200) { - return false; - } - - parameters.put(SCALE_KEY, Integer.toString(newScale)); - firePropertyChange("scale", oldScale, newScale); - return true; - } - - public boolean decrementScale() { - return setScale(Math.max(10, getScale() - 10)); // minimum 10 % - } - - public boolean incrementScale() { - return setScale(Math.min(200, getScale() + 10)); // maximum 200 % - } - - // property change handling - - public void addChangeListener(PropertyChangeListener listener) { - changeSupport.addPropertyChangeListener(listener); - } - - public void removeChangeListener(PropertyChangeListener listener) { - changeSupport.removePropertyChangeListener(listener); - } - - protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { - changeSupport.firePropertyChange(propertyName, oldValue, newValue); + public void setShowRepeatingEventsGrayed(boolean showEventsGrayed) { + parameters.put(SHOW_REPEATING_EVENTS_GRAYED_KEY, Boolean.toString(showEventsGrayed)); + firePropertyChange("parameter4", null, showEventsGrayed); } }
diff --git a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapGenerator.java b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapGenerator.java index 20e0a2f..c65f7aa 100644 --- a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapGenerator.java +++ b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapGenerator.java
@@ -41,28 +41,28 @@ throw new IllegalStateException("Utility class"); } - public static void updateDiagram(PlantUmlDiagram diagram, AbstractEventChain eventChain, EventChainMapConfig config) { + public static void updateDiagram(PlantUmlDiagram diagram, AbstractEventChain chain, EventChainMapConfig config) { + // reset old diagram data diagram.resetDiagramData(); // generate new diagram - - diagram.append("@startmindmap\n\n"); - diagram.append("' Created by EventChainMapGenerator (" + timestamp() + ")\n\n"); - - buildContent(diagram, eventChain, config); - - diagram.append("\n@endmindmap"); + buildContent(diagram, chain, config); } private static void buildContent(PlantUmlDiagram diagram, AbstractEventChain eventChain, EventChainMapConfig config) { - diagram.append("\n' ===== Event chain (as PlantUML mind map) =====\n\n"); + diagram.append("' ===== Event chain (as PlantUML mind map) =====\n\n"); + + diagram.append("@startmindmap\n\n"); List<AbstractEventChain> predecessors = new ArrayList<>(); List<Event> previousEvents = new ArrayList<>(); + createChain(predecessors, previousEvents, null, eventChain, "+", config, diagram); + + diagram.append("\n@endmindmap"); } private static void createChain(List<AbstractEventChain> predecessors, List<Event> previousEvents, EventChainItem chainItem, AbstractEventChain chain, String prefix, EventChainMapConfig config, PlantUmlDiagram diagram) { @@ -75,7 +75,7 @@ // chain final String chainName = getChainName(chain); final String chainType = getChainType(chain); - final String chainLink = getLinkToObject(chain, diagram); + final String chainLink = getLinkToObject(chain, config, diagram); final String chainIcon = (chainItem instanceof EventChainReference) ? REF_ICON : ""; diagram.append(prefix + " " + chainIcon + chainName + chainType + chainLink + "\n"); @@ -89,7 +89,7 @@ if (showEvents) { Event stimulus = chain.getStimulus(); String stimulusName = getEventName(stimulus, parent, chainItem, EventType.STIMULUS); - String stimulusLink = getLinkToObject(stimulus, diagram); + String stimulusLink = getLinkToObject(stimulus, config, diagram); if (config.isShowRepeatingEventsGrayed()) { stimulusName = updateEventName(stimulusName, stimulus, previousEvents); @@ -114,7 +114,7 @@ if (showEvents) { Event response = chain.getResponse(); String responseName = getEventName(response, parent, chainItem, EventType.RESPONSE); - String responseLink = getLinkToObject(response, diagram); + String responseLink = getLinkToObject(response, config, diagram); if (config.isShowRepeatingEventsGrayed()) { responseName = updateEventName(responseName, response, previousEvents); @@ -124,8 +124,8 @@ } } - private static String getLinkToObject(EObject eObj, PlantUmlDiagram diagram) { - if (eObj == null || diagram == null) + private static String getLinkToObject(EObject eObj, EventChainMapConfig config, PlantUmlDiagram diagram) { + if (eObj == null || diagram == null || !config.isShowLinks()) return ""; final String id = diagram.getOrCreateId(eObj);
diff --git a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapVisualization.java b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapVisualization.java index e2d41f6..12cff9c 100644 --- a/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapVisualization.java +++ b/plugins/org.eclipse.app4mc.amalthea.visualizations.svg/src/org/eclipse/app4mc/amalthea/visualizations/svg/EventChainMapVisualization.java
@@ -15,28 +15,18 @@ package org.eclipse.app4mc.amalthea.visualizations.svg; -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.function.Consumer; - import javax.annotation.PostConstruct; import org.eclipse.app4mc.amalthea.model.AbstractEventChain; import org.eclipse.app4mc.visualization.ui.VisualizationParameters; import org.eclipse.app4mc.visualization.ui.registry.Visualization; import org.eclipse.app4mc.visualization.util.svg.PlantUmlDiagram; -import org.eclipse.app4mc.visualization.util.svg.SvgUtil; -import org.eclipse.core.runtime.Platform; import org.eclipse.e4.core.services.events.IEventBroker; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.layout.RowLayoutFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; -import org.eclipse.swt.browser.LocationListener; -import org.eclipse.swt.custom.CLabel; -import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.osgi.service.component.annotations.Component; @@ -44,7 +34,7 @@ "name=Eventchain Map", "description=Map Visualization for Eventchains" }) -public class EventChainMapVisualization implements Visualization { +public class EventChainMapVisualization extends AbstractVisualization implements Visualization { /** * Entry point for the visualization framework @@ -62,20 +52,23 @@ IEventBroker broker) { // Create central context object with all relevant inputs - final Context context = new Context(eventChain, parameters); + final EventChainMapConfig config = new EventChainMapConfig(parameters); + final Context context = createContext(eventChain, config); Composite pane = new Composite(parent, SWT.NONE); GridLayoutFactory.fillDefaults().applyTo(pane); Composite buttonArea = new Composite(pane, SWT.NONE); addToggleButton(buttonArea, "Show All Event", "Also show events (stimulus and response) of parent chains", - context.config::setShowAllEvents, context.config.isShowAllEvents()); + config::setShowAllEvents, config.isShowAllEvents()); + addToggleButton(buttonArea, "Show Links", "Show links to model elements", + config::setShowLinks, config.isShowLinks()); addToggleButton(buttonArea, "Expand Subchain References", null, - context.config::setExpandSubchainReferences, context.config.isExpandSubchainReferences()); + config::setExpandSubchainReferences, config.isExpandSubchainReferences()); addToggleButton(buttonArea, "Gray out repeating events", null, - context.config::setShowRepeatingEventsGrayed, context.config.isShowRepeatingEventsGrayed()); + config::setShowRepeatingEventsGrayed, config.isShowRepeatingEventsGrayed()); - addZoomBox(buttonArea, context); + addZoomBox(buttonArea, context.config); RowLayoutFactory.swtDefaults().fill(true).applyTo(buttonArea); @@ -84,136 +77,27 @@ GridDataFactory.fillDefaults().align(SWT.FILL, SWT.FILL).grab(true, true).applyTo(browser); // Create and display content - updateContent(browser, context); + updateBrowserContent(browser, context); } - private Browser addBrowser(Composite pane, IEventBroker broker, final Context context) { - Browser browser = new Browser(pane, SWT.NONE); + // handling of generic context (and type casts) - // Setup navigation to a selected element in the model viewer - if (broker != null) { - browser.addLocationListener(LocationListener.changingAdapter(c -> { - c.doit = true; - - Object target = null; - int idx = c.location.lastIndexOf('#'); - if (idx >= 0) { - target = context.diagram.getObjectById(c.location.substring(idx + 1)); - } - if (target != null) { - HashMap<String, Object> data = new HashMap<>(); - data.put("modelElements", Collections.singletonList(target)); - broker.send("org/eclipse/app4mc/amalthea/editor/SELECT", data); - - c.doit = false; - } - })); - } - - // React to configuration parameter changes - context.config.addChangeListener(e -> { - if (e.getPropertyName().equals("scale")) - updateSvgScale(browser, (int) e.getNewValue()); - if (e.getPropertyName().startsWith("parameter")) - updateContent(browser, context); - }); - - return browser; + private Context createContext(AbstractEventChain eventChain, EventChainMapConfig config) { + return new Context( + eventChain, + config, + new PlantUmlDiagram()); } - /** - * Helper for adding a toggle button - * - * @param parent container element - * @param text label text - * @param toolTip label tool tip - * @param f button select action; takes the button's selection status as an argument - * @param initialSelected initial selection state of the button - */ - private void addToggleButton(Composite parent, String text, String toolTip, final Consumer<Boolean> f, boolean initialSelected) { - final Button btn = new Button(parent, SWT.TOGGLE | SWT.FLAT); - btn.setText(text); - btn.setToolTipText(toolTip); - btn.setSelection(initialSelected); - btn.addListener(SWT.Selection, e -> f.accept(btn.getSelection())); - } + protected void updateDiagram(Context context) { + if (context.diagram instanceof PlantUmlDiagram + && context.object instanceof AbstractEventChain + && context.config instanceof EventChainMapConfig) { - private void addZoomBox(Composite buttonArea, Context context) { - // Align the box with the other buttons - final Composite zoomArea = new Composite(buttonArea, SWT.NONE); - RowLayoutFactory.fillDefaults().margins(1, 1).applyTo(zoomArea); - - final Composite box = new Composite(zoomArea, SWT.BORDER); - - final Button btnLeft = new Button(box, SWT.ARROW | SWT.LEFT | SWT.FLAT); - btnLeft.addListener(SWT.Selection, e -> context.config.decrementScale()); - - final CLabel scaleLabel = new CLabel(box, SWT.FLAT | SWT.CENTER); - scaleLabel.setText(String.format("%d %%", context.config.getScale())); // set initial label text - - context.config.addChangeListener(e -> { - if (e.getPropertyName().equals("scale")) - scaleLabel.setText(String.format("%d %%", (int) e.getNewValue())); // update label text - }); - - final Button btnRight = new Button(box, SWT.ARROW | SWT.RIGHT | SWT.FLAT); - btnRight.addListener(SWT.Selection, e -> context.config.incrementScale()); - - RowLayoutFactory.fillDefaults().fill(true).applyTo(box); - } - - private void updateSvgScale(Browser browser, int newScale) { - // Update SVG size in browser via JavaScript/DOM - if (browser != null) { - browser.execute(SvgUtil.buildUpdateScaleCommand(newScale)); - } - } - - /** - * Visualizes a given model element in a browser. - * <p> - * The plantUML graph is constructed and compiled in a separate thread. - * - * @param browser - * @param context - */ - private void updateContent(Browser browser, Context context) { - new Thread(() -> { - // Build PlantUML diagram text - EventChainMapGenerator.updateDiagram(context.diagram, context.eventChain, context.config); - - // Render to SVG - String result; - try { - result = context.diagram.renderToSvg(); - } catch (IOException e) { - result = "Error invoking PlantUML: \"" + e.getMessage() - + "\". Make sure you have configured the path to the dot executable properly in the PlantUML preferences."; - Platform.getLog(EventChainMapVisualization.class).error(result, e); - return; - } - - // Apply initial scale and display - if (result != null && !browser.isDisposed()) { - final String browserContent = SvgUtil.initiallyApplyScale(result, context.config.getScale()); - browser.getDisplay().asyncExec(() -> { - if (!browser.isDisposed()) { - browser.setText(browserContent); - } - }); - } - - }).start(); - } - - static class Context { - public final AbstractEventChain eventChain; - public final EventChainMapConfig config; - public final PlantUmlDiagram diagram = new PlantUmlDiagram(); - - public Context(AbstractEventChain eventChain, VisualizationParameters viewParameters) { - this.eventChain = eventChain; - this.config = new EventChainMapConfig(viewParameters); + EventChainMapGenerator.updateDiagram( + (PlantUmlDiagram) context.diagram, + (AbstractEventChain) context.object, + (EventChainMapConfig) context.config); } }
diff --git a/plugins/org.eclipse.app4mc.visualization.util.svg/src/org/eclipse/app4mc/visualization/util/svg/AbstractDiagram.java b/plugins/org.eclipse.app4mc.visualization.util.svg/src/org/eclipse/app4mc/visualization/util/svg/AbstractDiagram.java index e52172e..ca0f77d 100644 --- a/plugins/org.eclipse.app4mc.visualization.util.svg/src/org/eclipse/app4mc/visualization/util/svg/AbstractDiagram.java +++ b/plugins/org.eclipse.app4mc.visualization.util.svg/src/org/eclipse/app4mc/visualization/util/svg/AbstractDiagram.java
@@ -15,6 +15,8 @@ package org.eclipse.app4mc.visualization.util.svg; +import java.io.IOException; + import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.InstanceScope; @@ -24,7 +26,7 @@ import net.sourceforge.plantuml.eclipse.utils.PlantumlConstants; -abstract class AbstractDiagram { +public abstract class AbstractDiagram { private StringBuilder diagramBuilder = new StringBuilder(); private BiMap<Object, String> idMap = HashBiMap.create(); @@ -97,4 +99,6 @@ } } + public abstract String renderToSvg() throws IOException; + }