diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index d07b6588d..1e7baf45d 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -1097,10 +1097,8 @@ TCMotorSelPan.noDescription = No description available. ! PlotDialog -PlotDialog.title.Flightdataplot = Flight data plot -PlotDialog.Chart.Simulatedflight = Simulated flight PlotDialog.CheckBox.Showdatapoints = Show data points -PlotDialog.lbl.Chart = Click and drag down+right to zoom in, up+left to zoom out +PlotDialog.lbl.Chart = mouse wheel to zoom. alt-mouse wheel to zoom x axis only. drag to pan. ! "main" prefix is used for the main application dialog diff --git a/core/src/net/sf/openrocket/gui/plot/SimulationChart.java b/core/src/net/sf/openrocket/gui/plot/SimulationChart.java new file mode 100644 index 000000000..fb5f14e63 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/plot/SimulationChart.java @@ -0,0 +1,238 @@ +package net.sf.openrocket.gui.plot; + +import java.awt.Cursor; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.Serializable; + +import org.jfree.chart.ChartPanel; +import org.jfree.chart.ChartRenderingInfo; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.Pannable; +import org.jfree.chart.plot.PiePlot; +import org.jfree.chart.plot.Plot; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.PlotRenderingInfo; +import org.jfree.chart.plot.Zoomable; + +import com.jogamp.newt.event.InputEvent; + +public class SimulationChart extends ChartPanel { + + private Point2D panLast; + private double panW; + private double panH; + + private MouseWheelHandler mouseWheelHandler = null; + + public SimulationChart(JFreeChart chart, boolean properties, boolean save, + boolean print, boolean zoom, boolean tooltips) { + super(chart, properties, save, print, zoom, tooltips); + } + + public SimulationChart(JFreeChart chart, boolean useBuffer) { + super(chart, useBuffer); + } + + public SimulationChart(JFreeChart chart, int width, int height, + int minimumDrawWidth, int minimumDrawHeight, int maximumDrawWidth, + int maximumDrawHeight, boolean useBuffer, boolean properties, + boolean copy, boolean save, boolean print, boolean zoom, + boolean tooltips) { + super(chart, width, height, minimumDrawWidth, minimumDrawHeight, + maximumDrawWidth, maximumDrawHeight, useBuffer, properties, copy, save, + print, zoom, tooltips); + } + + public SimulationChart(JFreeChart chart, int width, int height, + int minimumDrawWidth, int minimumDrawHeight, int maximumDrawWidth, + int maximumDrawHeight, boolean useBuffer, boolean properties, + boolean save, boolean print, boolean zoom, boolean tooltips) { + super(chart, width, height, minimumDrawWidth, minimumDrawHeight, + maximumDrawWidth, maximumDrawHeight, useBuffer, properties, save, + print, zoom, tooltips); + } + + public SimulationChart(JFreeChart chart) { + super(chart); + } + + @Override + public boolean isMouseWheelEnabled() { + return mouseWheelHandler != null; + } + + @Override + public void setMouseWheelEnabled(boolean flag) { + if ( flag && mouseWheelHandler == null ) { + this.mouseWheelHandler = new MouseWheelHandler(); + this.addMouseWheelListener(this.mouseWheelHandler); + } else if ( !flag && mouseWheelHandler != null ) { + this.removeMouseWheelListener(this.mouseWheelHandler); + this.mouseWheelHandler = null; + } + } + + @Override + public void mousePressed(MouseEvent e) { + if ( e.getButton() == MouseEvent.BUTTON1 ) { + // if no modifiers, use pan + Rectangle2D screenDataArea = getScreenDataArea(e.getX(), e.getY()); + + if ( screenDataArea != null && screenDataArea.contains(e.getPoint())) { + this.panW = screenDataArea.getWidth(); + this.panH = screenDataArea.getHeight(); + this.panLast = e.getPoint(); + setCursor( Cursor.getPredefinedCursor( Cursor.MOVE_CURSOR)); + } + } else if ( e.getButton() == MouseEvent.BUTTON2 ) { + + } + } + + @Override + public void mouseDragged(MouseEvent e) { + if ( panLast == null ) { + return; + } + + double dx = e.getX() - this.panLast.getX(); + double dy = e.getY() - this.panLast.getY(); + if ( dx == 0.0 && dy == 0.0 ) { + return ; + } + double wPercent = -dx / this.panW; + double hPercent = dy / this.panH; + boolean old = this.getChart().getPlot().isNotify(); + this.getChart().getPlot().setNotify(false); + Pannable p = (Pannable) this.getChart().getPlot(); + if ( p.getOrientation() == PlotOrientation.VERTICAL){ + p.panDomainAxes( wPercent, this.getChartRenderingInfo().getPlotInfo(),panLast); + p.panRangeAxes( hPercent, this.getChartRenderingInfo().getPlotInfo(),panLast); + } else { + p.panDomainAxes( hPercent, this.getChartRenderingInfo().getPlotInfo(),panLast); + p.panRangeAxes( wPercent, this.getChartRenderingInfo().getPlotInfo(),panLast); + } + + this.panLast = e.getPoint(); + this.getChart().getPlot().setNotify(old); + } + + @Override + public void mouseReleased(MouseEvent e) { + if ( this.panLast != null ) { + this.panLast = null; + setCursor(Cursor.getDefaultCursor()); + } + } + + + /** + * + * Hacked up copy of MouseWheelHandler from JFreechart. This version + * has the special ability to only zoom on the domain if the alt key is pressed. + * + * A class that handles mouse wheel events for the {@link ChartPanel} class. + * Mouse wheel event support was added in JDK 1.4, so this class will be omitted + * from JFreeChart if you build it using JDK 1.3. + * + * @since 1.0.13 + */ + class MouseWheelHandler implements MouseWheelListener, Serializable { + + /** The zoom factor. */ + double zoomFactor; + + /** + * Creates a new instance for the specified chart panel. + * + * @param chartPanel the chart panel (null not permitted). + */ + public MouseWheelHandler() { + this.zoomFactor = 0.10; + } + + /** + * Returns the current zoom factor. The default value is 0.10 (ten + * percent). + * + * @return The zoom factor. + * + * @see #setZoomFactor(double) + */ + public double getZoomFactor() { + return this.zoomFactor; + } + + /** + * Sets the zoom factor. + * + * @param zoomFactor the zoom factor. + * + * @see #getZoomFactor() + */ + public void setZoomFactor(double zoomFactor) { + this.zoomFactor = zoomFactor; + } + + /** + * Handles a mouse wheel event from the underlying chart panel. + * + * @param e the event. + */ + public void mouseWheelMoved(MouseWheelEvent e) { + JFreeChart chart = SimulationChart.this.getChart(); + if (chart == null) { + return; + } + Plot plot = chart.getPlot(); + if (plot instanceof Zoomable) { + boolean domainOnly = ( e.getModifiers() & InputEvent.ALT_MASK ) != 0; + Zoomable zoomable = (Zoomable) plot; + handleZoomable(zoomable, e, domainOnly); + } + else if (plot instanceof PiePlot) { + PiePlot pp = (PiePlot) plot; + pp.handleMouseWheelRotation(e.getWheelRotation()); + } + } + + /** + * Handle the case where a plot implements the {@link Zoomable} interface. + * + * @param zoomable the zoomable plot. + * @param e the mouse wheel event. + */ + private void handleZoomable(Zoomable zoomable, MouseWheelEvent e, boolean domainOnly) { + // don't zoom unless the mouse pointer is in the plot's data area + ChartRenderingInfo info = SimulationChart.this.getChartRenderingInfo(); + PlotRenderingInfo pinfo = info.getPlotInfo(); + Point2D p = SimulationChart.this.translateScreenToJava2D(e.getPoint()); + if (!pinfo.getDataArea().contains(p)) { + return; + } + + Plot plot = (Plot) zoomable; + // do not notify while zooming each axis + boolean notifyState = plot.isNotify(); + plot.setNotify(false); + int clicks = e.getWheelRotation(); + double zf = 1.0 + this.zoomFactor; + if (clicks < 0) { + zf = 1.0 / zf; + } + if (SimulationChart.this.isDomainZoomable()) { + zoomable.zoomDomainAxes(zf, pinfo, p, true); + } + if (SimulationChart.this.isRangeZoomable() && !domainOnly ) { + zoomable.zoomRangeAxes(zf, pinfo, p, true); + } + plot.setNotify(notifyState); // this generates the change event too + } + + } + +} diff --git a/core/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java b/core/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java index b53c58ad2..3eb4d7da5 100644 --- a/core/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java +++ b/core/src/net/sf/openrocket/gui/plot/SimulationPlotDialog.java @@ -13,6 +13,7 @@ import java.awt.Stroke; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.InputEvent; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; @@ -37,6 +38,7 @@ import net.miginfocom.swing.MigLayout; import net.sf.openrocket.document.Simulation; import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.simulation.FlightDataBranch; import net.sf.openrocket.simulation.FlightDataType; @@ -137,7 +139,7 @@ public class SimulationPlotDialog extends JDialog { private SimulationPlotDialog(Window parent, Simulation simulation, PlotConfiguration config) { //// Flight data plot - super(parent, trans.get("PlotDialog.title.Flightdataplot")); + super(parent, simulation.getName()); this.setModalityType(ModalityType.DOCUMENT_MODAL); final boolean initialShowPoints = Application.getPreferences().getBoolean(Preferences.PLOT_SHOW_POINTS, false); @@ -200,7 +202,7 @@ public class SimulationPlotDialog extends JDialog { // Create the chart using the factory to get all default settings JFreeChart chart = ChartFactory.createXYLineChart( //// Simulated flight - trans.get("PlotDialog.Chart.Simulatedflight"), + simulation.getName(), null, null, null, @@ -214,6 +216,9 @@ public class SimulationPlotDialog extends JDialog { // Add the data and formatting to the plot XYPlot plot = chart.getXYPlot(); + plot.setDomainPannable(true); + plot.setRangePannable(true); + int axisno = 0; for (int i = 0; i < 2; i++) { // Check whether axis has any data @@ -408,7 +413,7 @@ public class SimulationPlotDialog extends JDialog { JPanel panel = new JPanel(new MigLayout("fill")); this.add(panel); - ChartPanel chartPanel = new ChartPanel(chart, + final ChartPanel chartPanel = new SimulationChart(chart, false, // properties true, // save false, // print @@ -422,6 +427,10 @@ public class SimulationPlotDialog extends JDialog { panel.add(chartPanel, "grow, wrap 20lp"); + //// Description text + JLabel label = new StyledLabel(trans.get("PlotDialog.lbl.Chart"), -2); + panel.add(label, "gapleft para"); + //// Show data points final JCheckBox check = new JCheckBox(trans.get("PlotDialog.CheckBox.Showdatapoints")); check.setSelected(initialShowPoints); @@ -437,15 +446,42 @@ public class SimulationPlotDialog extends JDialog { }); panel.add(check, "split, left"); + //// Zoom out button + JButton button = new JButton(Icons.ZOOM_OUT); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if ( (e.getModifiers() & InputEvent.ALT_MASK) == InputEvent.ALT_MASK ) { + chartPanel.actionPerformed( new ActionEvent( chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_OUT_DOMAIN_COMMAND)); + } else { + chartPanel.actionPerformed( new ActionEvent( chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_OUT_BOTH_COMMAND)); + } + } + }); + panel.add(button, "gapleft rel"); - JLabel label = new StyledLabel(trans.get("PlotDialog.lbl.Chart"), -2); - panel.add(label, "gapleft para"); + //// Zoom in button + button = new JButton(Icons.ZOOM_IN); + button.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if ( (e.getModifiers() & InputEvent.ALT_MASK) == InputEvent.ALT_MASK ) { + chartPanel.actionPerformed( new ActionEvent( chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_IN_DOMAIN_COMMAND)); + } else { + chartPanel.actionPerformed( new ActionEvent( chartPanel, ActionEvent.ACTION_FIRST, ChartPanel.ZOOM_IN_BOTH_COMMAND)); + } + } + }); + panel.add(button, "gapleft rel"); + //// Add series selection box + //// FIXME + panel.add(new JPanel(), "growx"); //// Close button - JButton button = new JButton(trans.get("dlg.but.close")); + button = new JButton(trans.get("dlg.but.close")); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -519,13 +555,13 @@ public class SimulationPlotDialog extends JDialog { private static class ModifiedXYItemRenderer extends XYLineAndShapeRenderer { private final int branchCount; - + private ModifiedXYItemRenderer( int branchCount ) { this.branchCount = branchCount; } - - + + @Override public Paint lookupSeriesPaint(int series) { return super.lookupSeriesPaint(series/branchCount);