From e8c259807b4cb4227e9ebf504cbddb378ea493b8 Mon Sep 17 00:00:00 2001 From: Jordan Date: Thu, 5 Mar 2015 18:47:51 -0500 Subject: [PATCH] Added a new ConnectionPlotRender as a sub-class of PlotRender and a parent class of ParaViewPlotRender. It performs error handling for IConnectionAdapters and only sends requests to render to the sub-class if the connection is valid. Made the icon in the PlotRender's infoComposite customizable by sub-classes. Converted the ConnectionPlot to be a sub-class of MultiPlot and a parent class of ParaViewPlot. Signed-off-by: Jordan --- .../viz/service/paraview/ParaViewPlot.java | 22 +- .../service/paraview/ParaViewPlotRender.java | 58 +- .../eclipse/ice/viz/service/MultiPlot.java | 14 +- .../eclipse/ice/viz/service/PlotRender.java | 18 +- .../service/connections/ConnectionPlot.java | 510 +----------------- .../connections/ConnectionPlotRender.java | 281 ++++++++++ 6 files changed, 383 insertions(+), 520 deletions(-) create mode 100644 src/org.eclipse.ice.viz.service/src/org/eclipse/ice/viz/service/connections/ConnectionPlotRender.java diff --git a/src/org.eclipse.ice.viz.service.paraview/src/org/eclipse/ice/viz/service/paraview/ParaViewPlot.java b/src/org.eclipse.ice.viz.service.paraview/src/org/eclipse/ice/viz/service/paraview/ParaViewPlot.java index 8d6f73503..be9b700fb 100644 --- a/src/org.eclipse.ice.viz.service.paraview/src/org/eclipse/ice/viz/service/paraview/ParaViewPlot.java +++ b/src/org.eclipse.ice.viz.service.paraview/src/org/eclipse/ice/viz/service/paraview/ParaViewPlot.java @@ -14,13 +14,13 @@ import java.net.URI; import java.util.HashMap; import java.util.Map; -import java.util.Random; -import org.eclipse.ice.viz.service.MultiPlot; import org.eclipse.ice.viz.service.PlotRender; -import org.eclipse.swt.graphics.Color; +import org.eclipse.ice.viz.service.connections.ConnectionPlot; import org.eclipse.swt.widgets.Composite; +import com.kitware.vtk.web.VtkWebClient; + /** * This class is responsible for embedding ParaView-supported graphics inside * client {@link Composite}s. @@ -34,7 +34,7 @@ * @author Jordan Deyton * */ -public class ParaViewPlot extends MultiPlot { +public class ParaViewPlot extends ConnectionPlot { /** * The default constructor. @@ -55,6 +55,7 @@ public ParaViewPlot(ParaViewVizService vizService, URI file) { */ @Override public Map getPlotTypes() throws Exception { + // TODO return new HashMap(); } @@ -83,17 +84,4 @@ protected void updatePlotRender(PlotRender plotRender) { super.updatePlotRender(plotRender); } - // /* - // * (non-Javadoc) - // * - // * @see - // * - // org.eclipse.ice.viz.service.connections.ConnectionPlot#getPreferenceNodeID - // * () - // */ - // @Override - // protected String getPreferenceNodeID() { - // return "org.eclipse.ice.viz.service.paraview.preferences"; - // } - } diff --git a/src/org.eclipse.ice.viz.service.paraview/src/org/eclipse/ice/viz/service/paraview/ParaViewPlotRender.java b/src/org.eclipse.ice.viz.service.paraview/src/org/eclipse/ice/viz/service/paraview/ParaViewPlotRender.java index 755c5d051..6f75fde70 100644 --- a/src/org.eclipse.ice.viz.service.paraview/src/org/eclipse/ice/viz/service/paraview/ParaViewPlotRender.java +++ b/src/org.eclipse.ice.viz.service.paraview/src/org/eclipse/ice/viz/service/paraview/ParaViewPlotRender.java @@ -2,37 +2,81 @@ import java.util.Random; -import org.eclipse.ice.viz.service.PlotRender; +import org.eclipse.ice.viz.service.connections.ConnectionPlotRender; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.widgets.Composite; -public class ParaViewPlotRender extends PlotRender { +import com.kitware.vtk.web.VtkWebClient; +public class ParaViewPlotRender extends ConnectionPlotRender { + + /** + * The default constructor. + * + * @param parent + * The parent Composite that contains the plot render. + * + * @param plot + * The rendered ConnectionPlot. This cannot be changed. + */ public ParaViewPlotRender(Composite parent, ParaViewPlot plot) { super(parent, plot); } + /* + * (non-Javadoc) + * + * @see org.eclipse.ice.viz.service.connections.ConnectionPlotRender# + * createPlotComposite(org.eclipse.swt.widgets.Composite, int, + * java.lang.Object) + */ @Override - protected Composite createPlotComposite(Composite parent, int style) - throws Exception { - // TODO Hook this up to the ParaView widgets. + protected Composite createPlotComposite(Composite parent, int style, + VtkWebClient connection) throws Exception { return new Composite(parent, style); } + /* + * (non-Javadoc) + * + * @see org.eclipse.ice.viz.service.connections.ConnectionPlotRender# + * updatePlotComposite(org.eclipse.swt.widgets.Composite, java.lang.Object) + */ @Override - protected void updatePlotComposite(Composite plotComposite) - throws Exception { + protected void updatePlotComposite(Composite plotComposite, + VtkWebClient connection) throws Exception { + // TODO Hook this up to the ParaView widgets. int seed = (getPlotCategory() + getPlotType()).hashCode(); Random r = new Random(seed); plotComposite.setBackground(new Color(plotComposite.getDisplay(), r .nextInt(255), r.nextInt(255), r.nextInt(255))); + + return; } + /* + * (non-Javadoc) + * + * @see + * org.eclipse.ice.viz.service.PlotRender#disposePlotComposite(org.eclipse + * .swt.widgets.Composite) + */ @Override protected void disposePlotComposite(Composite plotComposite) { // Nothing to do yet. } + /* + * (non-Javadoc) + * + * @see org.eclipse.ice.viz.service.connections.ConnectionPlotRender# + * getPreferenceNodeID() + */ + @Override + protected String getPreferenceNodeID() { + return "org.eclipse.ice.viz.service.paraview.preferences"; + } + } diff --git a/src/org.eclipse.ice.viz.service/src/org/eclipse/ice/viz/service/MultiPlot.java b/src/org.eclipse.ice.viz.service/src/org/eclipse/ice/viz/service/MultiPlot.java index 0c2a63b18..b6518ddd0 100644 --- a/src/org.eclipse.ice.viz.service/src/org/eclipse/ice/viz/service/MultiPlot.java +++ b/src/org.eclipse.ice.viz.service/src/org/eclipse/ice/viz/service/MultiPlot.java @@ -1,7 +1,9 @@ package org.eclipse.ice.viz.service; import java.net.URI; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.eclipse.ice.client.widgets.viz.service.IPlot; @@ -206,7 +208,7 @@ protected void setDataSource(URI file) { * * @return The visualization service responsible for this plot. */ - protected IVizService getVizService() { + public IVizService getVizService() { return vizService; } @@ -233,6 +235,16 @@ protected IVizService getVizService() { protected void updatePlotRender(PlotRender plotRender) { plotRender.refresh(); } + + /** + * Gets a list of all current rendered plots. + * + * @return A list containing each current {@link PlotRender} in this + * {@code MultiPlot}. + */ + protected List getPlotRenders() { + return new ArrayList(plotRenders.values()); + } // -------------------- // } diff --git a/src/org.eclipse.ice.viz.service/src/org/eclipse/ice/viz/service/PlotRender.java b/src/org.eclipse.ice.viz.service/src/org/eclipse/ice/viz/service/PlotRender.java index 20a104130..8188675d2 100644 --- a/src/org.eclipse.ice.viz.service/src/org/eclipse/ice/viz/service/PlotRender.java +++ b/src/org.eclipse.ice.viz.service/src/org/eclipse/ice/viz/service/PlotRender.java @@ -74,6 +74,11 @@ public abstract class PlotRender { */ private Label msgLabel; + /** + * The image to display in the {@link #iconLabel}. + */ + protected Image infoIcon; + // -------------------- // /** @@ -152,7 +157,7 @@ public String getPlotType() { * update to the UI on the display's UI thread. *

*/ - protected void refresh() { + public void refresh() { // If we are not on the UI thread, update the UI asynchronously on // the UI thread. @@ -305,16 +310,19 @@ protected Composite createInfoComposite(Composite parent, int style) { protected void updateInfoComposite(Composite infoComposite, final String message) { // Set the message and icon based on the state of the connection. - final Image image; final Display display = infoComposite.getDisplay(); - - // Set a default image. - image = display.getSystemImage(SWT.ICON_WARNING); + // If there's no icon set, default to something useful. + final Image image = (infoIcon != null ? infoIcon : display + .getSystemImage(SWT.ICON_WARNING)); // Update the contents of the infoComposite's widgets. iconLabel.setImage(image); msgLabel.setText(message); + // Force the StackLayout to refresh. We need the two boolean flags so + // that the text will wrap properly. + stackComposite.layout(true, true); + return; } diff --git a/src/org.eclipse.ice.viz.service/src/org/eclipse/ice/viz/service/connections/ConnectionPlot.java b/src/org.eclipse.ice.viz.service/src/org/eclipse/ice/viz/service/connections/ConnectionPlot.java index 54283c5cd..6c0069e71 100644 --- a/src/org.eclipse.ice.viz.service/src/org/eclipse/ice/viz/service/connections/ConnectionPlot.java +++ b/src/org.eclipse.ice.viz.service/src/org/eclipse/ice/viz/service/connections/ConnectionPlot.java @@ -1,26 +1,12 @@ package org.eclipse.ice.viz.service.connections; import java.net.URI; -import java.util.HashMap; -import java.util.Map; import org.eclipse.ice.client.widgets.viz.service.IPlot; import org.eclipse.ice.client.widgets.viz.service.IVizService; import org.eclipse.ice.datastructures.ICEObject.IUpdateable; -import org.eclipse.swt.SWT; -import org.eclipse.swt.SWTException; -import org.eclipse.swt.custom.StackLayout; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.ui.dialogs.PreferencesUtil; -import org.eclipse.ui.forms.events.HyperlinkEvent; -import org.eclipse.ui.forms.events.IHyperlinkListener; -import org.eclipse.ui.forms.widgets.Hyperlink; +import org.eclipse.ice.viz.service.MultiPlot; +import org.eclipse.ice.viz.service.PlotRender; /** * This class provides the basic implementation for an {@link IPlot} whose @@ -32,70 +18,13 @@ * @param * The type of the connection object. */ -public abstract class ConnectionPlot implements IPlot, IConnectionClient { +public abstract class ConnectionPlot extends MultiPlot implements + IConnectionClient { - // TODO We should support drawing the same plot in multiple locations! - // TODO Provide access to plot properties. - - // ---- Service and Connection ---- // - /** - * The visualization service responsible for this plot. - */ - private final IVizService vizService; /** * The current connection adapter associated with this client. */ private IConnectionAdapter adapter; - // -------------------------------- // - - // ---- Source and Plot Properties ---- // - /** - * The data source, either a local or remote file. - */ - private URI source; - - /** - * The category of the currently drawn plot. - */ - private String plotCategory = null; - /** - * The type of the currently drawn plot. - */ - private String plotType = null; - // ------------------------------------ // - - // ---- UI Widgets ---- // - /** - * This composite contains the {@link #canvas} and {@link #infoComposite} in - * a stack. - */ - private Composite plotComposite = null; - /** - * The current widget used to draw the plot. This should only be visible if - * the connection is open. - */ - private Composite canvas = null; - /** - * This presents the user with helpful information about the status of the - * associated connection. It should only be visible if the connection is - * *not* open. - */ - private Composite infoComposite = null; - /** - * Displays an icon to demonstrate the severity of the message. - */ - private Label iconLabel; - /** - * Displays a message explaining the current state of the connection or why - * it cannot render anything. - */ - private Label msgLabel; - /** - * A link to the visualization connection preferences. - */ - private Hyperlink link; - - // -------------------- // /** * The default constructor. @@ -106,420 +35,9 @@ public abstract class ConnectionPlot implements IPlot, IConnectionClient { * The data source, either a local or remote file. */ public ConnectionPlot(IVizService vizService, URI file) { - // Check the parameters. - if (vizService == null) { - throw new NullPointerException("IPlot error: " - + "Null viz service not allowed."); - } - - this.vizService = vizService; - - // Set the data source now. This should build any required meta data. - setDataSource(file); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.ice.client.widgets.viz.service.IPlot#draw(java.lang.String, - * java.lang.String, org.eclipse.swt.widgets.Composite) - */ - @Override - public void draw(String category, String plotType, Composite parent) - throws Exception { - - // Get the current parent control. - Composite currentParent = (plotComposite != null ? plotComposite - .getParent() : null); - - // Check the parameters. - if (category == null || plotType == null || parent == null) { - throw new NullPointerException("IPlot error: " - + "Null arguments are not allowed when drawing plot."); - } else if (parent.isDisposed()) { - throw new SWTException(SWT.ERROR_WIDGET_DISPOSED, "IPlot error: " - + "Cannot draw plot inside disposed Composite."); - } else if (currentParent != null && parent != currentParent) { - throw new IllegalArgumentException("IPlot error: " - + "Cannot draw same plot in multiple Composites."); - } - - // Check the plot category and type. - String[] plotTypes = getPlotTypes().get(category); - if (plotTypes == null) { - throw new IllegalArgumentException("IPlot error: " - + "Invalid plot category \"" + category + "\"."); - } else { - boolean found = false; - for (int i = 0; !found && i < plotTypes.length; i++) { - found = plotType.equals(plotTypes[i]); - } - if (!found) { - throw new IllegalArgumentException("IPlot error: " - + "Invalid plot type \"" + plotType + "\"."); - } - } + super(vizService, file); - // Update the plot category and type. - this.plotCategory = category; - this.plotType = plotType; - - // Create the plot Composite. - plotComposite = new Composite(parent, SWT.NONE); - plotComposite.setFont(parent.getFont()); - plotComposite.setBackground(parent.getBackground()); - plotComposite.setLayout(new StackLayout()); - - // Trigger an update to the UI. - updateUI(); - - return; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ice.client.widgets.viz.service.IPlot#getProperties() - */ - @Override - public Map getProperties() { - return new HashMap(); - } - - /* - * (non-Javadoc) - * - * @see - * org.eclipse.ice.client.widgets.viz.service.IPlot#setProperties(java.util - * .Map) - */ - @Override - public void setProperties(Map props) throws Exception { - // Nothing to do yet. - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ice.client.widgets.viz.service.IPlot#getDataSource() - */ - @Override - public URI getDataSource() { - return source; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ice.client.widgets.viz.service.IPlot#isSourceRemote() - */ - @Override - public boolean isSourceRemote() { - return "localhost".equals(getSourceHost()); - } - - /** - * Sets the data source (which is currently rendered if the plot is drawn). - * If the data source is valid and new, then the plot will be updated - * accordingly. - * - * @param uri - * The new data source URI. - */ - protected void setDataSource(URI file) { - if (file != null) { - source = file; - } - } - - /** - * This method updates the UI component of the plot based on the current - * state of the VizService. This method should be used any time a UI update - * needs to be triggered, e.g., when the data source changes or when the - * underlying connection is updated. - */ - private void updateUI() { - - // Determine the current connection and its state. - final ConnectionState state; - final T connection; - if (adapter != null) { - state = adapter.getState(); - connection = adapter.getConnection(); - } else { - state = ConnectionState.Disconnected; - connection = null; - } - - // Trigger an update to the UI. - if (plotComposite != null && !plotComposite.isDisposed()) { - // If we are not on the UI thread, update the UI asynchronously on - // the UI thread. - if (Display.getCurrent() == null) { - plotComposite.getDisplay().asyncExec(new Runnable() { - @Override - public void run() { - updateUI(state, connection); - } - }); - } - // If we are on the UI thread, update the UI synchronously. - else { - updateUI(state, connection); - } - } - - return; - } - - /** - * This method updates the plot based on the current state of the - * VizService. This method should not be used. Instead, use - * {@link #updateUI()}. - *

- * Note: This method assumes that it is called on the UI thread! - *

- */ - private void updateUI(ConnectionState state, T connection) { - - final Display display = plotComposite.getDisplay(); - - // Get the StackLayout from the plot Composite. - final StackLayout stackLayout = (StackLayout) plotComposite.getLayout(); - - // This should never happen, but if it does, we should report an error - // and not break! - if (connection == null && state == ConnectionState.Connected) { - System.err - .println("IPlot error: " - + "The connection is not available, although it was reported as connected."); - state = ConnectionState.Failed; - } - - // If connected, try to render the plot in the canvas. - if (state == ConnectionState.Connected) { - - // If necessary, create the canvas. - if (canvas == null) { - // Create the canvas. - canvas = createCanvas(plotComposite, SWT.DOUBLE_BUFFERED, - connection); - } - - // Try to update the canvas. If an error occurs, show the info - // Composite with a useful message. - try { - updateCanvas(canvas, connection); - - // Make the Canvas appear in front. This only needs to be done - // if it's not already at the top. - if (stackLayout.topControl != canvas) { - stackLayout.topControl = canvas; - plotComposite.layout(); - } - } catch (Exception e) { - // Set the message and icon. - String message = e.getMessage(); - Image image = display.getSystemImage(SWT.ICON_WARNING); - - // If necessary, create the infoComposite. - if (infoComposite == null) { - infoComposite = createInfoComposite(plotComposite, SWT.NONE); - } - // Update the contents of the infoComposite's widgets. - iconLabel.setImage(image); - msgLabel.setText(message); - // Hide the link since the problem is not a connection issue. - link.setVisible(false); - // Make the infoComposite appear in front. - stackLayout.topControl = infoComposite; - plotComposite.layout(true, true); - // The above layout requires the two boolean flags to make sure - // the text widgets are also laid out. - } - } - // Otherwise, there is a problem of some sort. Give the user a link to - // the viz service preferences along with an informative message. - else { - - // Set the message and icon based on the state of the connection. - final String message; - final Image image; - final String serviceName = vizService.getName(); - if (state == ConnectionState.Connecting) { - message = "The " + serviceName - + " connection is being established..."; - image = display.getSystemImage(SWT.ICON_WORKING); - } else if (state == ConnectionState.Disconnected) { - if (connection == null) { - message = "The " + serviceName - + " connection is not configured."; - } else { - message = "The " + serviceName - + " connection is currently disconnected."; - } - image = display.getSystemImage(SWT.ICON_WARNING); - } else { - message = "The " + serviceName + " connection failed!"; - image = display.getSystemImage(SWT.ICON_ERROR); - } - - // If necessary, dispose the canvas and create the infoComposite. - if (canvas != null && !canvas.isDisposed()) { - canvas.dispose(); - canvas = null; - } - - // If necessary, create the infoComposite. - if (infoComposite == null) { - infoComposite = createInfoComposite(plotComposite, SWT.NONE); - } - // Update the contents of the infoComposite's widgets. - iconLabel.setImage(image); - msgLabel.setText(message); - // Show the link since the problem is a connection issue. - link.setVisible(true); - // Make the infoComposite appear in front. - stackLayout.topControl = infoComposite; - plotComposite.layout(true, true); - // The above layout requires the two boolean flags to make sure the - // text widgets are also laid out. - } - - return; - } - - /** - * This creates the useful information {@code Composite} that appears when - * the plot could not be rendered either due to an invalid or disconnected - * connection adapter or an invalid file. - * - * @param parent - * The parent {@code Composite} that will contain the info - * {@code Composite}. - * @param style - * The SWT style to use for the info {@code Composite}. - * @return The new info {@code Composite}. - */ - private Composite createInfoComposite(Composite parent, int style) { - - final Display display = parent.getDisplay(); - final Shell shell = parent.getShell(); - - Composite infoComposite = new Composite(parent, style); - infoComposite.setLayout(new GridLayout(2, false)); - - String linkText = "Click here to update the " + vizService.getName() - + " connection preferences."; - - // Create an info label with an image. - iconLabel = new Label(infoComposite, SWT.NONE); - iconLabel.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, - false, false)); - - // Create a Composite to contain the info message and the - // hyperlink - // with the info message above the hyperlink. - Composite msgComposite = new Composite(infoComposite, SWT.NONE); - msgComposite.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, - false, false)); - msgComposite.setLayout(new GridLayout(1, false)); - - // Create an info label with informative text. - msgLabel = new Label(msgComposite, SWT.NONE); - msgLabel.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, - false)); - - // Create a link to the preference page. - link = new Hyperlink(msgComposite, SWT.NONE); - link.setText(linkText); - link.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false)); - link.setUnderlined(true); - link.setForeground(display.getSystemColor(SWT.COLOR_LINK_FOREGROUND)); - // Add the listener to redirect the user to the preferences. - link.addHyperlinkListener(new IHyperlinkListener() { - @Override - public void linkEntered(HyperlinkEvent e) { - // Nothing to do yet. - } - - @Override - public void linkExited(HyperlinkEvent e) { - // Nothing to do yet. - } - - @Override - public void linkActivated(HyperlinkEvent e) { - // Open up the viz service connection preferences. - PreferencesUtil.createPreferenceDialogOn(shell, - getPreferenceNodeID(), null, null).open(); - } - }); - - return infoComposite; - } - - /** - * Gets the ID of the associated viz service's preferences node. This is - * used in the {@link #infoComposite}'s {@link #link} to the preferences. - * - * @return The preference page ID. - */ - protected abstract String getPreferenceNodeID(); - - /** - * Creates a new canvas that can render components via the associated - * connection. - * - * @param parent - * The parent {@code Composite} that will contain the canvas. - * @param style - * The SWT style to use for the canvas widget. - * @param connection - * The connection used to populate the canvas graphics. - * @return The new canvas. - */ - protected abstract Composite createCanvas(Composite parent, int style, - T connection); - - /** - * Updates what is currently displayed in the canvas if necessary. - * - * @param connection - * The connection used to render the plot. - * @throws Exception - * If the canvas could not be updated, then an exception should - * be thrown with an informative message. - */ - protected abstract void updateCanvas(Composite canvas, T connection) - throws Exception; - - /** - * Gets the category of the currently drawn plot. - * - * @return The category of the currently drawn plot. - */ - protected String getPlotCategory() { - return plotCategory; - } - - /** - * Gets the type of the currently drawn plot. - * - * @return The type of the currently drawn plot. - */ - protected String getPlotType() { - return plotType; - } - - /** - * Gets the visualization service responsible for this plot. - * - * @return The visualization service responsible for this plot. - */ - protected IVizService getVizService() { - return vizService; + // Nothing else to do yet. } // ---- Implements IConnectionClient (and IUpdateableListener) ---- // @@ -562,9 +80,21 @@ public void update(IUpdateable component) { // If the argument is null, then do nothing. Even if the current adapter // is null, the UI should already be up to date! if (component != null && component == adapter) { - // Trigger an update to the UI. - updateUI(); + // Trigger an update to the UI for all currently rendered plots. + for (PlotRender plotRender : getPlotRenders()) { + plotRender.refresh(); + } } } + // ---------------------------------------------------------------- // + + /** + * Gets the adapter for the current connection associated with this plot. + * + * @return The {@link #adapter}. This may be null. + */ + protected IConnectionAdapter getConnectionAdapter() { + return adapter; + } } diff --git a/src/org.eclipse.ice.viz.service/src/org/eclipse/ice/viz/service/connections/ConnectionPlotRender.java b/src/org.eclipse.ice.viz.service/src/org/eclipse/ice/viz/service/connections/ConnectionPlotRender.java new file mode 100644 index 000000000..411f030d1 --- /dev/null +++ b/src/org.eclipse.ice.viz.service/src/org/eclipse/ice/viz/service/connections/ConnectionPlotRender.java @@ -0,0 +1,281 @@ +package org.eclipse.ice.viz.service.connections; + +import org.eclipse.ice.viz.service.PlotRender; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.dialogs.PreferencesUtil; +import org.eclipse.ui.forms.events.HyperlinkEvent; +import org.eclipse.ui.forms.events.IHyperlinkListener; +import org.eclipse.ui.forms.widgets.Hyperlink; + +public abstract class ConnectionPlotRender extends PlotRender { + + // TODO Make the image/icon in the PlotRender class customizable. + + /** + * The rendered {@code ConnectionPlot}. This cannot be changed. + */ + public final ConnectionPlot plot; + + // ---- UI Widgets ---- // + /** + * A link to the visualization connection preferences. + */ + private Hyperlink link; + + /** + * Whether or not to show the {@link #link}. This should only be set to true + * when there is a connection problem when creating or updating the plot + * {@code Composite}. + */ + private boolean showLink = false; + + // -------------------- // + + /** + * The default constructor. + * + * @param parent + * The parent {@code Composite} that contains the plot render. + * @param plot + * The rendered {@code ConnectionPlot}. This cannot be changed. + */ + public ConnectionPlotRender(Composite parent, ConnectionPlot plot) { + super(parent, plot); + + // Keep a reference to the plot as a ConnectionPlot with the right type. + this.plot = plot; + } + + /** + * Adds a {@link #link} to the visualization service's connection + * preferences after the message label. + */ + @Override + protected Composite createInfoComposite(Composite parent, int style) { + + // Get the info Composite and its child with the message label. + final Composite infoComposite = super + .createInfoComposite(parent, style); + final Composite msgComposite = (Composite) infoComposite.getChildren()[1]; + + // Get a Display and Shell used to create the hyperlink. + final Display display = infoComposite.getDisplay(); + final Shell shell = infoComposite.getShell(); + + // Set the text to display in the hyperlink. + final String linkText = "Click here to update the " + + plot.getVizService().getName() + " connection preferences."; + + // Create a link to the preference page. + link = new Hyperlink(msgComposite, SWT.NONE); + link.setText(linkText); + link.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false)); + link.setUnderlined(true); + link.setForeground(display.getSystemColor(SWT.COLOR_LINK_FOREGROUND)); + // Add the listener to redirect the user to the preferences. + link.addHyperlinkListener(new IHyperlinkListener() { + @Override + public void linkEntered(HyperlinkEvent e) { + // Nothing to do yet. + } + + @Override + public void linkExited(HyperlinkEvent e) { + // Nothing to do yet. + } + + @Override + public void linkActivated(HyperlinkEvent e) { + // Open up the viz service connection preferences. + PreferencesUtil.createPreferenceDialogOn(shell, + getPreferenceNodeID(), null, null).open(); + } + }); + + return infoComposite; + } + + /** + * Updates the visibility of the {@link #link} in addition to the default + * update behavior. + */ + @Override + protected void updateInfoComposite(Composite infoComposite, String message) { + + // Set the link's visibility. + link.setVisible(showLink); + + super.updateInfoComposite(infoComposite, message); + }; + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.ice.viz.service.PlotRender#disposeInfoComposite(org.eclipse + * .swt.widgets.Composite) + */ + @Override + protected void disposeInfoComposite(Composite infoComposite) { + super.disposeInfoComposite(infoComposite); + + // We need to unset the link. + link = null; + } + + /** + * Gets the ID of the associated viz service's preferences node. This is + * used in the info {@code Composite}'s {@link #link} to the preferences. + * + * @return The preference page ID. + */ + protected abstract String getPreferenceNodeID(); + + /** + * Checks the current connection's status before re-directing to + * {@link #createPlotComposite(Composite, int, Object)}. + *

+ * Note: Sub-classes should not override this method. + *

+ */ + protected Composite createPlotComposite(Composite parent, int style) + throws Exception { + + // The default return value. + Composite plotComposite = null; + + // Validate the current state of the connection. This throws an + // exception if there's a connection problem. + IConnectionAdapter adapter = plot.getConnectionAdapter(); + validateConnection(adapter); + + // Try to render the plot. This may also throw an exception depending on + // the sub-class' implementation. + plotComposite = createPlotComposite(parent, style, + adapter.getConnection()); + + return plotComposite; + } + + /** + * Validates the connection, returning if the connection is valid or + * throwing an exception otherwise. + * + * @param adapter + * The {@link #plot}'s connection adapter. + * @throws Exception + * An exception with an informative message is thrown if there + * is a problem with the connection. + */ + protected void validateConnection(IConnectionAdapter adapter) + throws Exception { + // Get the connection and its state from the connection adapter. + final T connection = adapter.getConnection(); + final ConnectionState state = adapter.getState(); + + // Set the message and icon based on the state of the connection. + final String message; + final Image image; + final String serviceName = plot.getVizService().getName(); + + // Get the display from the parent Composite. This is required to set + // the error icon appropriately. + final Display display = parent.getDisplay(); + + // If the connection is valid, we should immediately break and return. + // This is expected to be the most common situation. + if (connection != null && state == ConnectionState.Connected) { + // There does not appear to be an issue related to the connection + // preferences, so hide the link. + showLink = false; + return; + } else if (state == ConnectionState.Connecting) { + message = "The " + serviceName + + " connection is being established..."; + image = display.getSystemImage(SWT.ICON_WORKING); + } else if (state == ConnectionState.Disconnected) { + if (connection == null) { + message = "The " + serviceName + + " connection is not configured."; + } else { + message = "The " + serviceName + + " connection is currently disconnected."; + } + image = display.getSystemImage(SWT.ICON_WARNING); + } else if (state == ConnectionState.Failed) { + message = "The " + serviceName + " connection failed!"; + image = display.getSystemImage(SWT.ICON_ERROR); + } else { // (connection == null) + message = "The " + serviceName + " connection is not available!"; + image = display.getSystemImage(SWT.ICON_ERROR); + } + + // Set the image and then throw an exception. + showLink = true; + infoIcon = image; + throw new Exception(message); + } + + /** + * Creates the plot {@code Composite} that is shown when the associated + * {@link #plot}, {@link #category}, and {@link #type} are all valid. + * + * @param parent + * The parent in which the plot {@code Composite} should be + * created. + * @param style + * The style to use for the plot {@code Composite}. + * @param connection + * The current connection used to render the plot. + * @return The new plot {@code Composite}. + * @throws Exception + * If the plot is in an invalid state or otherwise cannot be + * rendered, this throws an exception with an informative + * message. + */ + protected abstract Composite createPlotComposite(Composite parent, + int style, T connection) throws Exception; + + /** + * Checks the current connection's status before re-directing to + * {@link #updatePlotComposite(Composite, Object)}. + *

+ * Note: Sub-classes should not override this method. + *

+ */ + protected void updatePlotComposite(Composite plotComposite) + throws Exception { + + // Validate the current state of the connection. This throws an + // exception if there's a connection problem. + IConnectionAdapter adapter = plot.getConnectionAdapter(); + validateConnection(adapter); + + // Try to update the plot. This may also throw an exception depending on + // the sub-class' implementation. + updatePlotComposite(plotComposite, adapter.getConnection()); + + return; + } + + /** + * Updates the plot rendering contained in the specified plot + * {@code Composite}. + * + * @param plotComposite + * The plot {@code Composite} to update. + * @param connection + * The current connection used to render the plot. + * @throws Exception + * If the plot is in an invalid state or otherwise cannot be + * rendered, this throws an exception with an informative + * message. + */ + protected abstract void updatePlotComposite(Composite plotComposite, + T connection) throws Exception; +}