001/* A panel for controlling the format of a plotter.
002
003 Copyright (c) 1998-2018 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026 */
027package ptolemy.plot;
028
029import java.awt.BorderLayout;
030import java.awt.Container;
031import java.awt.Frame;
032import java.util.ArrayList;
033import java.util.Enumeration;
034import java.util.Vector;
035
036import javax.swing.JPanel;
037
038import ptolemy.gui.ComponentDialog;
039import ptolemy.gui.Query;
040import ptolemy.gui.QueryListener;
041
042///////////////////////////////////////////////////////////////////
043//// PlotFormatter
044
045/**
046
047 PlotFormatter is a panel that controls the format of a plotter object
048 passed to the constructor.
049
050 @see Plot
051 @see PlotBox
052 @author Edward A. Lee
053 @version $Id$
054 @since Ptolemy II 1.0
055 @Pt.ProposedRating Yellow (eal)
056 @Pt.AcceptedRating Red (cxh)
057 */
058@SuppressWarnings("serial")
059public class PlotFormatter extends JPanel {
060    /** Construct a plot formatter for the specified plot object.
061     *  @param plot The specified plot object.
062     */
063    public PlotFormatter(PlotBox plot) {
064        super();
065        _plot = plot;
066
067        setLayout(new BorderLayout());
068        _wideQuery = new Query();
069        add(_wideQuery, BorderLayout.WEST);
070        _narrowQuery = new Query();
071        add(_narrowQuery, BorderLayout.EAST);
072
073        // Populate the wide query.
074        _wideQuery.setTextWidth(20);
075        _originalTitle = plot.getTitle();
076        _wideQuery.addLine("title", "Title", _originalTitle);
077
078        _originalCaptions = plot.getCaptions();
079        StringBuffer captionsString = new StringBuffer();
080        for (Enumeration captions = _originalCaptions.elements(); captions
081                .hasMoreElements();) {
082            if (captionsString.length() > 0) {
083                captionsString.append('\n');
084            }
085            captionsString.append((String) captions.nextElement());
086        }
087        _wideQuery.addTextArea("caption", "Caption", captionsString.toString());
088
089        _originalXLabel = plot.getXLabel();
090        _wideQuery.addLine("xlabel", "X Label", _originalXLabel);
091        _originalYLabel = plot.getYLabel();
092        _wideQuery.addLine("ylabel", "Y Label", _originalYLabel);
093        _originalXRange = plot.getXRange();
094        _wideQuery.addLine("xrange", "X Range",
095                "" + _originalXRange[0] + ", " + _originalXRange[1]);
096        _originalYRange = plot.getYRange();
097        _wideQuery.addLine("yrange", "Y Range",
098                "" + _originalYRange[0] + ", " + _originalYRange[1]);
099
100        String[] marks = { "none", "points", "dots", "various", "bigdots",
101                "pixels" };
102        _originalMarks = "none";
103
104        if (plot instanceof Plot) {
105            _originalMarks = ((Plot) plot).getMarksStyle();
106            _wideQuery.addRadioButtons("marks", "Marks", marks, _originalMarks);
107        }
108
109        _originalXTicks = plot.getXTicks();
110        _originalXTicksSpec = "";
111
112        if (_originalXTicks != null) {
113            StringBuffer buffer = new StringBuffer();
114            Vector positions = _originalXTicks[0];
115            Vector labels = _originalXTicks[1];
116
117            for (int i = 0; i < labels.size(); i++) {
118                if (buffer.length() > 0) {
119                    buffer.append(", ");
120                }
121
122                buffer.append(labels.elementAt(i).toString());
123                buffer.append(" ");
124                buffer.append(positions.elementAt(i).toString());
125            }
126
127            _originalXTicksSpec = buffer.toString();
128        }
129
130        _wideQuery.addLine("xticks", "X Ticks", _originalXTicksSpec);
131
132        _originalYTicks = plot.getYTicks();
133        _originalYTicksSpec = "";
134
135        if (_originalYTicks != null) {
136            StringBuffer buffer = new StringBuffer();
137            Vector positions = _originalYTicks[0];
138            Vector labels = _originalYTicks[1];
139
140            for (int i = 0; i < labels.size(); i++) {
141                if (buffer.length() > 0) {
142                    buffer.append(", ");
143                }
144
145                buffer.append(labels.elementAt(i).toString());
146                buffer.append(" ");
147                buffer.append(positions.elementAt(i).toString());
148            }
149
150            _originalYTicksSpec = buffer.toString();
151        }
152
153        _wideQuery.addLine("yticks", "Y Ticks", _originalYTicksSpec);
154
155        _originalGrid = plot.getGrid();
156        _narrowQuery.addCheckBox("grid", "Grid", _originalGrid);
157        _originalStems = false;
158        _originalConnected = null;
159
160        if (plot instanceof Plot) {
161            _originalStems = ((Plot) plot).getImpulses();
162            _narrowQuery.addCheckBox("stems", "Stems", _originalStems);
163            _saveConnected();
164            _narrowQuery.addCheckBox("connected", "Connect",
165                    ((Plot) plot).getConnected());
166        }
167
168        _originalColor = plot.getColor();
169        _narrowQuery.addCheckBox("color", "Use Color", _originalColor);
170
171        if (plot instanceof Plot) {
172            // This method is also called by Histogram,
173            //  and Histogram does not have getLineStyles()
174            _originalLineStyles = ((Plot) plot).getLineStyles();
175            _narrowQuery.addCheckBox("lineStyles", "Use Line Styles",
176                    _originalLineStyles);
177        }
178
179        // FIXME: setXLog() and setYLog() cause problems with
180        // dropped data if they are toggled after data is read in.
181        // This is because the log axis facility modifies the datasets
182        // in addPlotPoint() in Plot.java.  When this is fixed
183        // we can add the XLog and YLog facility to the Format menu
184        //
185        // _originalXLog = plot.getXLog();
186        //_narrowQuery.addCheckBox("xlog", "X Log", _originalXLog);
187        //if (_originalXTicks != null) {
188        //    _narrowQuery.setBoolean("xlog", false);
189        //    _narrowQuery.setEnabled("xlog", false);
190        //}
191        // _originalYLog = plot.getYLog();
192        //_narrowQuery.addCheckBox("ylog", "Y Log", _originalYLog);
193        //if (_originalYTicks != null) {
194        //    _narrowQuery.setBoolean("ylog", false);
195        //    _narrowQuery.setEnabled("ylog", false);
196        //}
197        // Attach listeners.
198        _wideQuery.addQueryListener(new QueryListener() {
199            @Override
200            public void changed(String name) {
201                if (name.equals("title")) {
202                    _plot.setTitle(_wideQuery.getStringValue("title"));
203                } else if (name.equals("caption")) {
204                    _plot.clearCaptions();
205                    String newCaption = _wideQuery.getStringValue("caption");
206                    String[] captionsArray = newCaption.split("\\n");
207                    for (String element : captionsArray) {
208                        _plot.read("captions: " + element);
209                    }
210                } else if (name.equals("xlabel")) {
211                    _plot.setXLabel(_wideQuery.getStringValue("xlabel"));
212                } else if (name.equals("ylabel")) {
213                    _plot.setYLabel(_wideQuery.getStringValue("ylabel"));
214                } else if (name.equals("xrange")) {
215                    _plot.read(
216                            "XRange: " + _wideQuery.getStringValue("xrange"));
217                } else if (name.equals("xticks")) {
218                    String spec = _wideQuery.getStringValue("xticks").trim();
219                    _plot.read("XTicks: " + spec);
220
221                    // FIXME: log axis format temporarily
222                    // disabled, see above.
223                    // if (spec.equals("")) {
224                    //    _narrowQuery.setEnabled("xlog", true);
225                    // } else {
226                    //    _narrowQuery.setBoolean("xlog", false);
227                    //    _narrowQuery.setEnabled("xlog", false);
228                    // }
229                } else if (name.equals("yticks")) {
230                    String spec = _wideQuery.getStringValue("yticks").trim();
231                    _plot.read("YTicks: " + spec);
232
233                    // FIXME: log axis format temporarily
234                    // disabled, see above.
235                    // if (spec.equals("")) {
236                    //    _narrowQuery.setEnabled("ylog", true);
237                    // } else {
238                    //    _narrowQuery.setBoolean("ylog", false);
239                    //    _narrowQuery.setEnabled("ylog", false);
240                    // }
241                } else if (name.equals("yrange")) {
242                    _plot.read(
243                            "YRange: " + _wideQuery.getStringValue("yrange"));
244                } else if (name.equals("marks")) {
245                    ((Plot) _plot)
246                            .setMarksStyle(_wideQuery.getStringValue("marks"));
247                }
248
249                _plot.repaint();
250            }
251        });
252
253        _narrowQuery.addQueryListener(new QueryListener() {
254            @Override
255            public void changed(String name) {
256                if (name.equals("grid")) {
257                    _plot.setGrid(_narrowQuery.getBooleanValue("grid"));
258                } else if (name.equals("stems")) {
259                    ((Plot) _plot)
260                            .setImpulses(_narrowQuery.getBooleanValue("stems"));
261                    _plot.repaint();
262                } else if (name.equals("color")) {
263                    _plot.setColor(_narrowQuery.getBooleanValue("color"));
264
265                    // FIXME: log axis format temporarily
266                    // disabled, see above.
267                    // } else if (name.equals("xlog")) {
268                    //    _plot.setXLog(_narrowQuery.getBooleanValue("xlog"));
269                    // } else if (name.equals("ylog")) {
270                    //    _plot.setYLog(_narrowQuery.getBooleanValue("ylog"));
271                } else if (name.equals("connected")) {
272                    _setConnected(_narrowQuery.getBooleanValue("connected"));
273                } else if (name.equals("lineStyles")) {
274                    ((Plot) _plot).setLineStyles(
275                            _narrowQuery.getBooleanValue("lineStyles"));
276                }
277
278                _plot.repaint();
279            }
280        });
281    }
282
283    ///////////////////////////////////////////////////////////////////
284    ////                         public methods                    ////
285
286    /** Apply currently specified values to the associated plot.
287     *  This requests a repaint of the plot.
288     */
289    public void apply() {
290        // Apply current values.
291        _plot.setTitle(_wideQuery.getStringValue("title"));
292        _plot.setXLabel(_wideQuery.getStringValue("xlabel"));
293        _plot.setYLabel(_wideQuery.getStringValue("ylabel"));
294        _plot.read("XRange: " + _wideQuery.getStringValue("xrange"));
295        _plot.read("YRange: " + _wideQuery.getStringValue("yrange"));
296        _plot.setGrid(_narrowQuery.getBooleanValue("grid"));
297        _plot.setColor(_narrowQuery.getBooleanValue("color"));
298        // FIXME: log axis format temporarily disable, see above.
299        // _plot.setXLog(_narrowQuery.getBooleanValue("xlog"));
300        // _plot.setYLog(_narrowQuery.getBooleanValue("ylog"));
301        if (_plot instanceof Plot) {
302            // This method is also called by Histogram,
303            //  and Histogram does not have lineStyles
304            Plot cplot = (Plot) _plot;
305            cplot.setLineStyles(_narrowQuery.getBooleanValue("lineStyles"));
306            cplot.setMarksStyle(_wideQuery.getStringValue("marks"));
307            cplot.setImpulses(_narrowQuery.getBooleanValue("stems"));
308            _setConnected(_narrowQuery.getBooleanValue("connected"));
309        }
310
311        // FIXME: log axis format temporarily disable, see above.
312        // String spec = _wideQuery.getStringValue("xticks").trim();
313        // _plot.read("XTicks: " + spec);
314        // if (spec.equals("")) {
315        //    _narrowQuery.setEnabled("xlog", true);
316        // } else {
317        //    _narrowQuery.setBoolean("xlog", false);
318        //    _narrowQuery.setEnabled("xlog", false);
319        // }
320        // spec = _wideQuery.getStringValue("yticks").trim();
321        // _plot.read("YTicks: " + spec);
322        // if (spec.equals("")) {
323        //    _narrowQuery.setEnabled("ylog", true);
324        // } else {
325        //    _narrowQuery.setBoolean("ylog", false);
326        //    _narrowQuery.setEnabled("ylog", false);
327        // }
328        _plot.repaint();
329    }
330
331    /** Open a format control window as a top-level, modal dialog.
332     */
333    public void openModal() {
334        String[] buttons = { "Apply", "Cancel" };
335
336        // NOTE: If the plot is in a top-level container that is a Frame
337        // (as opposed to an applet), then tell the dialog that the Frame
338        // owns the dialog.
339        Container toplevel = _plot.getTopLevelAncestor();
340        Frame frame = null;
341
342        if (toplevel instanceof Frame) {
343            frame = (Frame) toplevel;
344        }
345
346        ComponentDialog dialog = new ComponentDialog(frame, "Set plot format",
347                this, buttons);
348
349        if (dialog.buttonPressed().equals("Apply")) {
350            apply();
351        } else {
352            restore();
353        }
354    }
355
356    /** Restore the original configuration of the plot, and request a
357     *  a redraw.
358     */
359    public void restore() {
360        // Restore _original values.
361        _plot.setTitle(_originalTitle);
362        _plot.setCaptions(_originalCaptions);
363        _plot.setXLabel(_originalXLabel);
364        _plot.setYLabel(_originalYLabel);
365        _plot.setXRange(_originalXRange[0], _originalXRange[1]);
366        _plot.setYRange(_originalYRange[0], _originalYRange[1]);
367        _plot.setGrid(_originalGrid);
368        _plot.setColor(_originalColor);
369        // FIXME: log axis format temporarily disable, see above.
370        // _plot.setXLog(_originalXLog);
371        // _plot.setYLog(_originalYLog);
372        if (_plot instanceof Plot) {
373            // This method is also called by Histogram,
374            //  and Histogram does not have lineStyles
375            Plot cplot = (Plot) _plot;
376            cplot.setLineStyles(_originalLineStyles);
377            cplot.setMarksStyle(_originalMarks);
378            cplot.setImpulses(_originalStems);
379            _restoreConnected();
380        }
381
382        // FIXME: log axis format temporarily disabled, see above.
383        // _plot.read("XTicks: " + _originalXTicksSpec);
384        // if (_originalXTicksSpec.equals("")) {
385        //    _narrowQuery.setEnabled("xlog", true);
386        // } else {
387        //   _narrowQuery.setBoolean("xlog", false);
388        //    _narrowQuery.setEnabled("xlog", false);
389        // }
390        // _plot.read("YTicks: " + _originalYTicksSpec);
391        // if (_originalYTicksSpec.equals("")) {
392        //    _narrowQuery.setEnabled("ylog", true);
393        // } else {
394        //    _narrowQuery.setBoolean("ylog", false);
395        //    _narrowQuery.setEnabled("ylog", false);
396        // }
397        _plot.repaint();
398    }
399
400    ///////////////////////////////////////////////////////////////////
401    ////                         protected variables               ////
402
403    /** The plot object controlled by this formatter. */
404    protected final PlotBox _plot;
405
406    ///////////////////////////////////////////////////////////////////
407    ////                         private methods                   ////
408    // Save the current connected state of all the point currently in the
409    // plot.  NOTE: This method reaches into the protected members of
410    // the Plot class, taking advantage of the fact that this class is
411    // in the same package.
412    private void _saveConnected() {
413        ArrayList<ArrayList<PlotPoint>> points = ((Plot) _plot)._points;
414        _originalConnected = new boolean[points.size()][];
415        _originalPoints = new PlotPoint[points.size()][];
416
417        for (int dataset = 0; dataset < points.size(); dataset++) {
418            ArrayList<PlotPoint> pts = points.get(dataset);
419            _originalConnected[dataset] = new boolean[pts.size()];
420            _originalPoints[dataset] = new PlotPoint[pts.size()];
421
422            for (int i = 0; i < pts.size(); i++) {
423                PlotPoint pt = pts.get(i);
424                _originalConnected[dataset][i] = pt.connected;
425                _originalPoints[dataset][i] = pt;
426            }
427        }
428    }
429
430    // Set the current connected state of all the point in the
431    // plot.  NOTE: This method reaches into the protected members of
432    // the Plot class, taking advantage of the fact that this class is
433    // in the same package.
434    private void _setConnected(boolean value) {
435        //ArrayList<ArrayList<PlotPoint>> points = ((Plot) _plot)._points;
436
437        // Make sure the default matches.
438        ((Plot) _plot).setConnected(value);
439
440        // We don't change the individual points anymore, but when the
441        // we plot points we'll both look at the global connected state and
442        // the one of the individual points (both will be and'ed).
443        /*
444        boolean[][] result = new boolean[points.size()][];
445
446        for (int dataset = 0; dataset < points.size(); dataset++) {
447            ArrayList<PlotPoint> pts = points.get(dataset);
448            result[dataset] = new boolean[pts.size()];
449
450            boolean first = true;
451
452            for (int i = 0; i < pts.size(); i++) {
453                PlotPoint pt = pts.get(i);
454                pt.connected = value && !first;
455                first = false;
456            }
457        }
458         */
459    }
460
461    // Restore the connected state of all the point that were in the
462    // plot when their connected state was saved.
463    // NOTE: This method reaches into the protected members of
464    // the plot class, taking advantage of the fact that this class is
465    // in the same package.
466    private void _restoreConnected() {
467        for (int dataset = 0; dataset < _originalPoints.length; dataset++) {
468            for (int i = 0; i < _originalPoints[dataset].length; i++) {
469                PlotPoint pt = _originalPoints[dataset][i];
470                pt.connected = _originalConnected[dataset][i];
471            }
472        }
473    }
474
475    ///////////////////////////////////////////////////////////////////
476    ////                         private variables                 ////
477    // Query widgets.
478    private Query _wideQuery;
479
480    ///////////////////////////////////////////////////////////////////
481    ////                         private variables                 ////
482    // Query widgets.
483    private Query _narrowQuery;
484
485    // Original configuration of the plot.
486    private String _originalTitle;
487
488    // Original configuration of the plot.
489    private Vector _originalCaptions;
490
491    // Original configuration of the plot.
492    private String _originalXLabel;
493
494    // Original configuration of the plot.
495    private String _originalYLabel;
496
497    // Original configuration of the plot.
498    private String _originalMarks;
499
500    // Original configuration of the plot.
501    private String _originalXTicksSpec;
502
503    // Original configuration of the plot.
504    private String _originalYTicksSpec;
505
506    private double[] _originalXRange;
507
508    private double[] _originalYRange;
509
510    private Vector[] _originalXTicks;
511
512    private Vector[] _originalYTicks;
513
514    private boolean _originalGrid;
515
516    private boolean _originalLineStyles;
517
518    private boolean _originalStems;
519
520    private boolean _originalColor;
521
522    private boolean[][] _originalConnected;
523
524    private PlotPoint[][] _originalPoints;
525}