001/* Top-level window containing a plotter.
002
003 Copyright (c) 1998-2014 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.Color;
031import java.awt.Dimension;
032import java.awt.Event;
033import java.awt.Toolkit;
034import java.awt.event.ActionEvent;
035import java.awt.event.ActionListener;
036import java.awt.event.KeyEvent;
037import java.awt.print.PageFormat;
038import java.awt.print.PrinterException;
039import java.awt.print.PrinterJob;
040import java.beans.PropertyChangeEvent;
041import java.beans.PropertyChangeListener;
042import java.io.File;
043import java.io.FileInputStream;
044import java.io.FileNotFoundException;
045import java.io.FileOutputStream;
046import java.io.IOException;
047import java.io.InputStream;
048import java.io.OutputStream;
049import java.net.URL;
050
051import javax.print.PrintService;
052import javax.print.attribute.HashPrintRequestAttributeSet;
053import javax.print.attribute.PrintRequestAttributeSet;
054import javax.print.attribute.standard.Destination;
055import javax.swing.JFileChooser;
056import javax.swing.JFrame;
057import javax.swing.JMenu;
058import javax.swing.JMenuBar;
059import javax.swing.JMenuItem;
060import javax.swing.JOptionPane;
061import javax.swing.KeyStroke;
062import javax.swing.UIManager;
063import javax.swing.filechooser.FileFilter;
064
065import ptolemy.gui.ImageExportable;
066import ptolemy.util.StringUtilities;
067
068// TO DO:
069//   - Add a mechanism for combining two plots into one
070///////////////////////////////////////////////////////////////////
071//// PlotFrame
072
073/**
074
075 PlotFrame is a versatile two-dimensional data plotter that runs as
076 part of an application, but in its own window. It can read files
077 compatible with the old Ptolemy plot file format (currently only ASCII).
078 It is extended with the capability to read PlotML files in PlotMLFrame.
079 An application can also interact directly with the contained Plot
080 object, which is visible as a public member, by invoking its methods.
081 <p>
082 An application that uses this class should set up the handling of
083 window-closing events.  Presumably, the application will exit when
084 all windows have been closed. This is done with code something like:
085 <pre>
086 plotFrameInstance.addWindowListener(new WindowAdapter() {
087 public void windowClosing(WindowEvent e) {
088 // Handle the event
089 }
090 });
091 </pre>
092 <p>
093 PlotFrame contains an instance of PlotBox. PlotBox is the base class for
094 classes with drawing capability, e.g. Plot, LogicAnalyzer. If not
095 specified in the constructor, the default is to contain a Plot object. This
096 field is set once in the constructor and immutable afterwards.
097
098 @see Plot
099 @see PlotBox
100 @author Christopher Brooks and Edward A. Lee
101 @version $Id$
102 @since Ptolemy II 0.2
103 @Pt.ProposedRating Yellow (cxh)
104 @Pt.AcceptedRating Yellow (cxh)
105 */
106@SuppressWarnings("serial")
107public class PlotFrame extends JFrame
108        implements PropertyChangeListener, ImageExportable {
109    /** Construct a plot frame with a default title and by default contains
110     *  an instance of Plot. After constructing this, it is necessary
111     *  to call setVisible(true) to make the plot appear.
112     */
113    public PlotFrame() {
114        this("Ptolemy Plot Frame");
115    }
116
117    /** Construct a plot frame with the specified title and by default
118     *  contains an instance of Plot. After constructing this, it is necessary
119     *  to call setVisible(true) to make the plot appear.
120     *  @param title The title to put on the window.
121     */
122    public PlotFrame(String title) {
123        this(title, null);
124    }
125
126    /** Construct a plot frame with the specified title and the specified
127     *  instance of PlotBox.  After constructing this, it is necessary
128     *  to call setVisible(true) to make the plot appear.
129     *  @param title The title to put on the window.
130     *  @param plotArg the plot object to put in the frame, or null to create
131     *   an instance of Plot.
132     */
133    public PlotFrame(String title, PlotBox plotArg) {
134        super(title);
135
136        // The Java look & feel is pretty lame, so we use the native
137        // look and feel of the platform we are running on.
138        try {
139            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
140        } catch (Throwable throwable) {
141            // Ignore exceptions, which only result in the wrong look and feel.
142        }
143
144        if (plotArg == null) {
145            plot = new Plot();
146        } else {
147            plot = plotArg;
148        }
149
150        // Background color is a light grey.
151        plot.setBackground(new Color(0xe5e5e5));
152
153        _fileMenu.setMnemonic(KeyEvent.VK_F);
154        _editMenu.setMnemonic(KeyEvent.VK_E);
155        _specialMenu.setMnemonic(KeyEvent.VK_S);
156
157        // File menu
158        JMenuItem[] fileMenuItems = { new JMenuItem("Open", KeyEvent.VK_O),
159                new JMenuItem("Save", KeyEvent.VK_S),
160                new JMenuItem("SaveAs", KeyEvent.VK_A),
161                new JMenuItem("Export", KeyEvent.VK_E),
162                new JMenuItem("Print", KeyEvent.VK_P),
163                new JMenuItem("Close", KeyEvent.VK_C), };
164
165        // Open button = ctrl-o.
166        fileMenuItems[0].setAccelerator(
167                KeyStroke.getKeyStroke(KeyEvent.VK_O, Event.CTRL_MASK));
168
169        // Save button = ctrl-s.
170        fileMenuItems[1].setAccelerator(
171                KeyStroke.getKeyStroke(KeyEvent.VK_S, Event.CTRL_MASK));
172
173        // Print button = ctrl-p.
174        fileMenuItems[4].setAccelerator(
175                KeyStroke.getKeyStroke(KeyEvent.VK_P, Event.CTRL_MASK));
176
177        // Close button = ctrl-w.
178        fileMenuItems[5].setAccelerator(
179                KeyStroke.getKeyStroke(KeyEvent.VK_W, Event.CTRL_MASK));
180
181        FileMenuListener fml = new FileMenuListener();
182
183        // Set the action command and listener for each menu item.
184        for (JMenuItem fileMenuItem : fileMenuItems) {
185            fileMenuItem.setActionCommand(fileMenuItem.getText());
186            fileMenuItem.addActionListener(fml);
187            _fileMenu.add(fileMenuItem);
188        }
189
190        _menubar.add(_fileMenu);
191
192        // Edit menu
193        JMenuItem format = new JMenuItem("Format", KeyEvent.VK_F);
194        FormatListener formatListener = new FormatListener();
195        format.addActionListener(formatListener);
196        _editMenu.add(format);
197        _menubar.add(_editMenu);
198
199        // Special menu
200        JMenuItem[] specialMenuItems = { new JMenuItem("About", KeyEvent.VK_A),
201                new JMenuItem("Help", KeyEvent.VK_H),
202                new JMenuItem("Clear", KeyEvent.VK_C),
203                new JMenuItem("Fill", KeyEvent.VK_F),
204                new JMenuItem("Reset axes", KeyEvent.VK_R),
205                new JMenuItem("Sample plot", KeyEvent.VK_S), };
206        SpecialMenuListener sml = new SpecialMenuListener();
207
208        // Set the action command and listener for each menu item.
209        for (JMenuItem specialMenuItem : specialMenuItems) {
210            specialMenuItem.setActionCommand(specialMenuItem.getText());
211            specialMenuItem.addActionListener(sml);
212            _specialMenu.add(specialMenuItem);
213        }
214
215        _menubar.add(_specialMenu);
216
217        setJMenuBar(_menubar);
218
219        getContentPane().add(plot, BorderLayout.CENTER);
220
221        // FIXME: This should not be hardwired in here.
222        setSize(500, 300);
223
224        // Center.
225        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
226        Dimension frameSize = getSize();
227        int x = (screenSize.width - frameSize.width) / 2;
228        int y = (screenSize.height - frameSize.height) / 2;
229        setLocation(x, y);
230    }
231
232    ///////////////////////////////////////////////////////////////////
233    ////                         public methods                    ////
234
235    /** Respond to dialog action.
236     *  @param event The dialog event.
237     */
238    @Override
239    public void propertyChange(PropertyChangeEvent event) {
240        // System.out.println(event.paramString());
241        Object source = event.getSource();
242        if (source instanceof JFileChooser) {
243            FileFilter filter = ((JFileChooser) source).getFileFilter();
244            JFileChooser fileDialog = (JFileChooser) source;
245            if (filter instanceof EPSFileFilter) {
246                fileDialog.setSelectedFile(
247                        new File(fileDialog.getCurrentDirectory(), "plot.eps"));
248            } else if (filter instanceof FilterForGIF) {
249                fileDialog.setSelectedFile(
250                        new File(fileDialog.getCurrentDirectory(), "plot.gif"));
251            } else {
252                // FIXME: For some inexplicable reason, the following line does nothing if the
253                // directory already exists!!!!!!!!!!!!! Pretty lame...
254                fileDialog.setSelectedFile(
255                        new File(fileDialog.getCurrentDirectory(), "plot"));
256            }
257        }
258    }
259
260    /** Create a sample plot.
261     */
262    public void samplePlot() {
263        _file = null;
264        _directory = null;
265        plot.samplePlot();
266    }
267
268    /** Set the visibility.  As a side effect, this method
269     *  sets the background of the menus.
270     *  @param visible True if the Frame is to be visible, false
271     *  if it is not visible.
272     */
273    @Override
274    public void setVisible(boolean visible) {
275        super.setVisible(visible);
276        _editMenu.setBackground(_menubar.getBackground());
277        _fileMenu.setBackground(_menubar.getBackground());
278        _specialMenu.setBackground(_menubar.getBackground());
279    }
280
281    /** Write an image to the specified output stream in the specified
282     *  format.  Supported formats include at least "gif" and "png",
283     *  standard image file formats.  The image is a rendition of the
284     *  current view of the model.
285     *  @param stream The output stream to write to.
286     *  @param format The image format to generate.
287     *  @exception IOException If writing to the stream fails.
288     *  @exception PrinterException  If the specified format is not supported.
289     */
290    @Override
291    public void writeImage(OutputStream stream, String format)
292            throws PrinterException, IOException {
293        if (plot == null) {
294            throw new IOException("No plot to write image from!");
295        }
296        plot.exportImage(stream, format);
297    }
298
299    ///////////////////////////////////////////////////////////////////
300    ////                         public variables                  ////
301
302    /** The plot object held by this frame. */
303    public/*final*/PlotBox plot;
304
305    ///////////////////////////////////////////////////////////////////
306    ////                         protected variables               ////
307
308    /** Directory that contains the input file. */
309    protected File _directory = null;
310
311    /** Edit menu for this frame. */
312    protected JMenu _editMenu = new JMenu("Edit");
313
314    /** The input file. */
315    protected File _file = null;
316
317    /** File menu for this frame. */
318    protected JMenu _fileMenu = new JMenu("File");
319
320    /** Menubar for this frame. */
321    protected JMenuBar _menubar = new JMenuBar();
322
323    /** Special menu for this frame. */
324    protected JMenu _specialMenu = new JMenu("Special");
325
326    ///////////////////////////////////////////////////////////////////
327    ////                         protected methods                 ////
328
329    /** Display a menu that describes the Plotter. */
330    protected void _about() {
331        JOptionPane.showMessageDialog(this,
332                "PlotFrame class\n" + "By: Edward A. Lee "
333                        + "and Christopher Brooks\n" + "Version "
334                        + PlotBox.PTPLOT_RELEASE + ", Build: $Id$\n\n"
335                        + "For more information, see\n"
336                        + "http://ptolemy.eecs.berkeley.edu/java/ptplot\n\n"
337                        + "Copyright (c) 1997-2014, "
338                        + "The Regents of the University of California.",
339                "About Ptolemy Plot", JOptionPane.INFORMATION_MESSAGE);
340    }
341
342    /** Close the window.
343     */
344    protected void _close() {
345        dispose();
346    }
347
348    /** Interactively edit the file format in a modal dialog.
349     */
350    protected void _editFormat() {
351        PlotFormatter fmt = new PlotFormatter(plot);
352        fmt.openModal();
353    }
354
355    /** Query the user for a filename and export the plot to that file.
356     *  Currently, the supported formats are EPS and GIF.
357     */
358    protected void _export() {
359        JFileChooser fileDialog = new JFileChooser();
360        fileDialog.addChoosableFileFilter(new FolderForLatex());
361        fileDialog.addChoosableFileFilter(new EPSFileFilter());
362        fileDialog.addChoosableFileFilter(new FilterForGIF());
363        fileDialog.setDialogTitle("Export to...");
364
365        if (_directory != null) {
366            fileDialog.setCurrentDirectory(_directory);
367        } else {
368            // The default on Windows is to open at user.home, which is
369            // typically an absurd directory inside the O/S installation.
370            // So we use the current directory instead.
371            String cwd = StringUtilities.getProperty("user.dir");
372
373            if (cwd != null) {
374                fileDialog.setCurrentDirectory(new File(cwd));
375            }
376        }
377
378        fileDialog.setSelectedFile(
379                new File(fileDialog.getCurrentDirectory(), "plot.gif"));
380
381        fileDialog.addPropertyChangeListener(
382                JFileChooser.FILE_FILTER_CHANGED_PROPERTY, this);
383
384        int returnVal = fileDialog.showDialog(this, "Export");
385
386        fileDialog.removePropertyChangeListener(this);
387
388        if (returnVal == JFileChooser.APPROVE_OPTION) {
389            // Determine which export format is selected.
390            File file = fileDialog.getSelectedFile();
391            try {
392                FileOutputStream fout = null;
393
394                try {
395                    // With no filename extension, do Latex export.
396                    // Otherwise, do EPS.
397                    String name = file.getName();
398                    int position = name.lastIndexOf('.');
399                    String extension = "";
400                    if (position > 0) {
401                        extension = name.substring(position + 1);
402                    }
403                    if (extension.equals("")) {
404                        // No extension. Assume Latex export.
405                        plot.exportLatex(file);
406                    } else if (extension.equalsIgnoreCase("eps")) {
407                        fout = new FileOutputStream(file);
408                        plot.export(fout);
409                    } else {
410                        // Default is GIF export.
411                        fout = new FileOutputStream(file);
412                        plot.exportImage(fout, "gif");
413                    }
414                } finally {
415                    try {
416                        if (fout != null) {
417                            fout.close();
418                        }
419                    } catch (Throwable throwable) {
420                        System.out.println("Ignoring failure to close stream "
421                                + "on " + file);
422                        throwable.printStackTrace();
423                    }
424                }
425            } catch (IOException ex) {
426                JOptionPane.showMessageDialog(this,
427                        "Error exporting plot: " + ex, "Ptolemy II Error",
428                        JOptionPane.WARNING_MESSAGE);
429            }
430        }
431    }
432
433    /** Display more detailed information than given by _about().
434     */
435    protected void _help() {
436        JOptionPane.showMessageDialog(this,
437                "PlotFrame is a plot in a top-level window.\n"
438                        + "  File formats understood: Ptplot ASCII.\n"
439                        + "  Left mouse button: Zooming.",
440                "About Ptolemy Plot", JOptionPane.INFORMATION_MESSAGE);
441    }
442
443    /** Open a new file and plot its data.
444     */
445    protected void _open() {
446        JFileChooser fileDialog = new JFileChooser();
447        fileDialog.setDialogTitle("Select a plot file");
448
449        // Filter file names.
450        fileDialog.addChoosableFileFilter(new PLTOrXMLFileFilter());
451
452        if (_directory != null) {
453            fileDialog.setCurrentDirectory(_directory);
454        } else {
455            // The default on Windows is to open at user.home, which is
456            // typically an absurd directory inside the O/S installation.
457            // So we use the current directory instead.
458            String cwd = StringUtilities.getProperty("user.dir");
459
460            if (cwd != null) {
461                fileDialog.setCurrentDirectory(new File(cwd));
462            }
463        }
464
465        int returnVal = fileDialog.showOpenDialog(this);
466
467        if (returnVal == JFileChooser.APPROVE_OPTION) {
468            _file = fileDialog.getSelectedFile();
469            setTitle(_file.getName());
470            _directory = fileDialog.getCurrentDirectory();
471
472            FileInputStream input = null;
473            try {
474                plot.clear(true);
475                input = new FileInputStream(_file);
476                _read(new URL("file", null, _directory.getAbsolutePath()),
477                        input);
478                plot.repaint();
479            } catch (FileNotFoundException ex) {
480                JOptionPane.showMessageDialog(this,
481                        "File not found:\n" + ex.toString(),
482                        "Ptolemy Plot Error", JOptionPane.WARNING_MESSAGE);
483            } catch (IOException ex) {
484                JOptionPane.showMessageDialog(this,
485                        "Error reading input:\n" + ex.toString(),
486                        "Ptolemy Plot Error", JOptionPane.WARNING_MESSAGE);
487            } finally {
488                if (input != null) {
489                    try {
490                        input.close();
491                    } catch (Exception ex) {
492                        // Ignore this, but print anyway.
493                        ex.printStackTrace();
494                    }
495                }
496            }
497        }
498    }
499
500    /** Print the plot using the native interface.
501     */
502    protected void _print() {
503        // If you are using $PTII/bin/vergil, under bash, set this property:
504        // export JAVAFLAGS=-Dptolemy.ptII.print.platform=CrossPlatform
505        // and then run $PTII/bin/vergil
506        if (StringUtilities.getProperty("ptolemy.ptII.print.platform")
507                .equals("CrossPlatform")) {
508            _printCrossPlatform();
509        } else {
510            _printNative();
511        }
512    }
513
514    /** Print using the cross platform dialog.
515     *  Note that in java 1.6.0_05, the properties button is disabled,
516     *  so using _printNative() is preferred.
517     */
518    protected void _printCrossPlatform() {
519        // FIXME: Code duplication with PlotBox and Top.
520
521        // Note that this dialog used to be slow, but this was fixed in:
522        // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6539061
523        // Note that this dialog used to appear behind other windows,
524        // but this was fixed in:
525        // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4775862
526
527        // For more notes, see
528        // https://wiki.eecs.berkeley.edu/ptolemy/Ptolemy/PrintingFromJava
529
530        // Build a set of attributes
531        PrinterJob job = PrinterJob.getPrinterJob();
532        PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
533        // No need to call pageDialog, printDialog has that same tab
534        //PageFormat pageFormat = job.pageDialog(aset);
535        //PageFormat pageFormat = job.pageDialog(job.defaultPage());
536        //job.setPrintable(plot, pageFormat);
537        job.setPrintable(plot);
538
539        if (job.printDialog(aset)) {
540            try {
541                job.print(aset);
542            } catch (Exception ex) {
543                JOptionPane.showMessageDialog(this,
544                        "Printing failed:\n" + ex.toString(), "Print Error",
545                        JOptionPane.WARNING_MESSAGE);
546            }
547        }
548    }
549
550    /** If a PDF printer is available print to it.
551     *  @exception PrinterException If a printer with the string "PDF"
552     * cannot be found or if the job cannot be set to the PDF print
553     * service or if there is another problem printing.
554     */
555    protected void _printPDF() throws PrinterException {
556        // Find something that will print to PDF
557        boolean foundPDFPrinter = false;
558
559        PrintService pdfPrintService = null;
560        PrintService printServices[] = PrinterJob.lookupPrintServices();
561        for (PrintService printService : printServices) {
562            if (printService.getName().indexOf("PDF") != -1) {
563                foundPDFPrinter = true;
564                pdfPrintService = printService;
565            }
566        }
567
568        if (pdfPrintService == null) {
569            throw new PrinterException("Could not find a printer with the "
570                    + "string \"PDF\" in its name.");
571        }
572
573        PrinterJob job = PrinterJob.getPrinterJob();
574        job.setPrintService(pdfPrintService);
575        job.setPrintable(plot, job.defaultPage());
576
577        PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
578        // This gets ignored, but let's try it anyway
579        Destination destination = new Destination(new File("plot.pdf").toURI());
580        aset.add(destination);
581
582        job.print(aset);
583        if (foundPDFPrinter) {
584            System.out.println("Plot printed from command line. "
585                    + "Under MacOSX, look for "
586                    + "~/Desktop/Java Printing.pdf");
587        }
588    }
589
590    /** Print using the native dialog.
591     */
592    protected void _printNative() {
593        // FIXME: Code duplication with PlotBox and Top.
594
595        // Native printing used not honor the user's
596        // choice of portrait vs. landscape.
597
598        PrinterJob job = PrinterJob.getPrinterJob();
599        PageFormat pageFormat = job.pageDialog(job.defaultPage());
600        job.setPrintable(plot, pageFormat);
601
602        if (job.printDialog()) {
603            try {
604                // job.print() eventually
605                // calls PlotBox.print(Graphics, PageFormat)
606                job.print();
607            } catch (Exception ex) {
608                JOptionPane.showMessageDialog(this,
609                        "Printing failed:\n" + ex.toString(), "Print Error",
610                        JOptionPane.WARNING_MESSAGE);
611            }
612        }
613    }
614
615    /** Read the specified stream.  Derived classes may override this
616     *  to support other file formats.
617     *  @param base The base for relative file references, or null if
618     *   there are not relative file references.
619     *  @param in The input stream.
620     *  @exception IOException If the stream cannot be read.
621     */
622    protected void _read(URL base, InputStream in) throws IOException {
623        plot.read(in);
624    }
625
626    /** Save the plot to the current file, determined by the
627     *  and _file protected variable.
628     */
629    protected void _save() {
630        if (_file != null) {
631            FileOutputStream output = null;
632            try {
633                output = new FileOutputStream(_file);
634                plot.write(output);
635            } catch (IOException ex) {
636                JOptionPane.showMessageDialog(this,
637                        "Error writing file:\n" + ex.toString(),
638                        "Ptolemy Plot Error", JOptionPane.WARNING_MESSAGE);
639            } finally {
640                if (output != null) {
641                    try {
642                        output.close();
643                    } catch (Exception ex) {
644                        // Ignore, but print anyway.
645                        ex.printStackTrace();
646                    }
647                }
648            }
649        } else {
650            _saveAs();
651        }
652    }
653
654    /** Query the user for a filename and save the plot to that file.
655     */
656    protected void _saveAs() {
657        JFileChooser fileDialog = new JFileChooser();
658        fileDialog.addChoosableFileFilter(new PLTOrXMLFileFilter());
659        fileDialog.setDialogTitle("Save plot as...");
660
661        if (_directory != null) {
662            fileDialog.setCurrentDirectory(_directory);
663        } else {
664            // The default on Windows is to open at user.home, which is
665            // typically an absurd directory inside the O/S installation.
666            // So we use the current directory instead.
667            String cwd = StringUtilities.getProperty("user.dir");
668
669            if (cwd != null) {
670                fileDialog.setCurrentDirectory(new File(cwd));
671            }
672        }
673
674        fileDialog.setSelectedFile(
675                new File(fileDialog.getCurrentDirectory(), "plot.xml"));
676
677        // Under Java 1.6 and Mac OS X, showSaveDialog() ignores the filter.
678        int returnVal = fileDialog.showSaveDialog(this);
679
680        if (returnVal == JFileChooser.APPROVE_OPTION) {
681            _file = fileDialog.getSelectedFile();
682            setTitle(_file.getName());
683            _directory = fileDialog.getCurrentDirectory();
684            _save();
685        }
686    }
687
688    ///////////////////////////////////////////////////////////////////
689    ////                         inner classes                     ////
690    class FileMenuListener implements ActionListener {
691        @Override
692        public void actionPerformed(ActionEvent e) {
693            JMenuItem target = (JMenuItem) e.getSource();
694            String actionCommand = target.getActionCommand();
695
696            try {
697                if (actionCommand.equals("Open")) {
698                    _open();
699                } else if (actionCommand.equals("Save")) {
700                    _save();
701                } else if (actionCommand.equals("SaveAs")) {
702                    _saveAs();
703                } else if (actionCommand.equals("Export")) {
704                    _export();
705                } else if (actionCommand.equals("Print")) {
706                    _print();
707                } else if (actionCommand.equals("Close")) {
708                    _close();
709                }
710            } catch (Throwable throwable) {
711                // If we do not catch exceptions here, then they
712                // disappear to stdout, which is bad if we launched
713                // where there is no stdout visible.
714                JOptionPane.showMessageDialog(null,
715                        "File Menu Exception:\n" + throwable,
716                        "Ptolemy Plot Error", JOptionPane.WARNING_MESSAGE);
717            }
718
719            // NOTE: The following should not be needed, but there jdk1.3beta
720            // appears to have a bug in swing where repainting doesn't
721            // properly occur.
722            repaint();
723        }
724    }
725
726    class FormatListener implements ActionListener {
727        @Override
728        public void actionPerformed(ActionEvent e) {
729            try {
730                _editFormat();
731            } catch (Exception exception) {
732                // If we do not catch exceptions here, then they
733                // disappear to stdout, which is bad if we launched
734                // where there is no stdout visible.
735                System.out.println("Format Exception: " + exception);
736                exception.printStackTrace();
737                JOptionPane.showMessageDialog(null,
738                        "Format Exception:\n" + exception.toString(),
739                        "Ptolemy Plot Error", JOptionPane.WARNING_MESSAGE);
740
741            }
742
743            // NOTE: The following should not be needed, but there jdk1.3beta
744            // appears to have a bug in swing where repainting doesn't
745            // properly occur.
746            repaint();
747        }
748    }
749
750    class SpecialMenuListener implements ActionListener {
751        @Override
752        public void actionPerformed(ActionEvent e) {
753            JMenuItem target = (JMenuItem) e.getSource();
754            String actionCommand = target.getActionCommand();
755
756            try {
757                if (actionCommand.equals("About")) {
758                    _about();
759                } else if (actionCommand.equals("Help")) {
760                    _help();
761                } else if (actionCommand.equals("Fill")) {
762                    plot.fillPlot();
763                } else if (actionCommand.equals("Reset axes")) {
764                    plot.resetAxes();
765                } else if (actionCommand.equals("Clear")) {
766                    plot.clear(false);
767                    plot.repaint();
768                } else if (actionCommand.equals("Sample plot")) {
769                    plot.clear(true);
770                    samplePlot();
771                }
772            } catch (Exception exception) {
773                // If we do not catch exceptions here, then they
774                // disappear to stdout, which is bad if we launched
775                // where there is no stdout visible.
776                JOptionPane.showMessageDialog(null,
777                        "Special Menu Exception:\n" + exception.toString(),
778                        "Ptolemy Plot Error", JOptionPane.WARNING_MESSAGE);
779            }
780
781            // NOTE: The following should not be needed, but there jdk1.3beta
782            // appears to have a bug in swing where repainting doesn't
783            // properly occur.
784            repaint();
785        }
786    }
787
788    ///////////////////////////////////////////////////////////////////
789    ////                         inner classes                     ////
790
791    /** Display only .eps files */
792    static class EPSFileFilter extends FileFilter {
793        /** Accept only .eps files.
794         *  @param fileOrDirectory The file or directory to be checked.
795         *  @return true if the file is a directory, a .eps file
796         */
797        @Override
798        public boolean accept(File fileOrDirectory) {
799            if (fileOrDirectory.isDirectory()) {
800                return true;
801            }
802
803            String fileOrDirectoryName = fileOrDirectory.getName();
804            int dotIndex = fileOrDirectoryName.lastIndexOf('.');
805
806            if (dotIndex == -1) {
807                return false;
808            }
809
810            String extension = fileOrDirectoryName.substring(dotIndex);
811
812            if (extension.equalsIgnoreCase(".eps")) {
813                return true;
814            } else {
815                return false;
816            }
817        }
818
819        /**  The description of this filter */
820        @Override
821        public String getDescription() {
822            return "Encapsulated PostScript (.eps) files";
823        }
824    }
825
826    /** Display only folders for inserting latex files. */
827    static class FolderForLatex extends FileFilter {
828        /** Accept only folders.
829         *  @param fileOrDirectory The file or directory to be checked.
830         *  @return true if the file is a directory.
831         */
832        @Override
833        public boolean accept(File fileOrDirectory) {
834            if (fileOrDirectory.isDirectory()) {
835                return true;
836            }
837            return false;
838        }
839
840        /**  The description of this filter */
841        @Override
842        public String getDescription() {
843            return "Latex Export to a Folder";
844        }
845    }
846
847    /** Display only .gif files */
848    static class FilterForGIF extends FileFilter {
849        /** Accept only .gif files.
850         *  @param fileOrDirectory The file or directory to be checked.
851         *  @return true if the file is a directory, a .eps file
852         */
853        @Override
854        public boolean accept(File fileOrDirectory) {
855            if (fileOrDirectory.isDirectory()) {
856                return true;
857            }
858
859            String fileOrDirectoryName = fileOrDirectory.getName();
860            int dotIndex = fileOrDirectoryName.lastIndexOf('.');
861
862            if (dotIndex < 0) {
863                return false;
864            }
865
866            String extension = fileOrDirectoryName.substring(dotIndex);
867
868            if (extension.equalsIgnoreCase(".gif")) {
869                return true;
870            } else {
871                return false;
872            }
873        }
874
875        /**  The description of this filter */
876        @Override
877        public String getDescription() {
878            return "GIF Image File (.gif)";
879        }
880    }
881
882    /** Display only .plt and .xml files */
883    static class PLTOrXMLFileFilter extends FileFilter {
884        /** Accept only .plt or .xml files.
885         *  @param fileOrDirectory The file to be checked.
886         *  @return true if the file is a directory, a .plot or a .xml file.
887         */
888        @Override
889        public boolean accept(File fileOrDirectory) {
890            if (fileOrDirectory.isDirectory()) {
891                return true;
892            }
893
894            String fileOrDirectoryName = fileOrDirectory.getName();
895            int dotIndex = fileOrDirectoryName.lastIndexOf('.');
896
897            if (dotIndex == -1) {
898                return false;
899            }
900
901            String extension = fileOrDirectoryName.substring(dotIndex);
902
903            if (extension.equalsIgnoreCase(".plt")
904                    || extension.equalsIgnoreCase(".xml")) {
905                return true;
906            } else {
907                return false;
908            }
909        }
910
911        /**  The description of this filter */
912        @Override
913        public String getDescription() {
914            return ".plt and .xml files";
915        }
916    }
917}