001    /*
002     * Copyright 2002 - 2007 JEuclid, http://jeuclid.sf.net
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    /* $Id: MainFrame.java,v 0b66106c7ff7 2010/08/06 15:35:06 max $ */
018    
019    package net.sourceforge.jeuclid.app.mathviewer;
020    
021    import java.awt.BorderLayout;
022    import java.awt.Point;
023    import java.awt.Toolkit;
024    import java.awt.datatransfer.DataFlavor;
025    import java.awt.datatransfer.StringSelection;
026    import java.awt.datatransfer.Transferable;
027    import java.awt.event.ActionEvent;
028    import java.awt.event.ActionListener;
029    import java.awt.event.KeyEvent;
030    import java.io.File;
031    
032    import javax.swing.JCheckBoxMenuItem;
033    import javax.swing.JDialog;
034    import javax.swing.JFrame;
035    import javax.swing.JMenu;
036    import javax.swing.JMenuBar;
037    import javax.swing.JMenuItem;
038    import javax.swing.JOptionPane;
039    import javax.swing.JPanel;
040    import javax.swing.JPopupMenu;
041    import javax.swing.JScrollPane;
042    import javax.swing.JSplitPane;
043    import javax.swing.KeyStroke;
044    import javax.swing.ScrollPaneConstants;
045    import javax.swing.event.DocumentEvent;
046    import javax.swing.event.DocumentListener;
047    
048    import net.sourceforge.jeuclid.MathMLSerializer;
049    import net.sourceforge.jeuclid.biparser.BiTree;
050    import net.sourceforge.jeuclid.biparser.NonIncrementalElementException;
051    import net.sourceforge.jeuclid.biparser.ReparseException;
052    import net.sourceforge.jeuclid.biparser.SAXBiParser;
053    import net.sourceforge.jeuclid.biparser.TextPosition;
054    import net.sourceforge.jeuclid.context.LayoutContextImpl;
055    import net.sourceforge.jeuclid.context.Parameter;
056    import net.sourceforge.jeuclid.swing.CursorListener;
057    import net.sourceforge.jeuclid.swing.JMathComponent;
058    
059    import org.apache.batik.util.gui.xmleditor.XMLContext;
060    import org.apache.batik.util.gui.xmleditor.XMLEditorKit;
061    import org.apache.batik.util.gui.xmleditor.XMLTextEditor;
062    import org.w3c.dom.Document;
063    import org.w3c.dom.Node;
064    
065    /**
066     * Main frame for the MathViewer application.
067     * 
068     * @version $Revision: 0b66106c7ff7 $
069     */
070    // CHECKSTYLE:OFF
071    public class MainFrame extends JFrame implements CursorListener {
072        // CHECKSTYLE:OFF
073    
074        private static final int DEFAULT_HEIGHT = 400;
075    
076        private static final int DEFAULT_WIDTH = 700;
077    
078        private static final FileIO FILEIO = FileIO.getInstance();
079    
080        // /**
081        // * Logger for this class
082        // */
083        // currently unused.
084        // private static final Log LOGGER = LogFactory.getLog(MainFrame.class);
085    
086        private static final long serialVersionUID = 1L;
087    
088        private static final float FONT_SIZE_MULTIPLICATOR = 1.20f;
089    
090        private JPanel jContentPane;
091    
092        private JMenuBar jJMenuBar;
093    
094        private JMenu fileMenu;
095    
096        private JMenu editMenu;
097    
098        private JMenu helpMenu;
099    
100        private JMenuItem exitMenuItem;
101    
102        private JMenuItem unformattedCopyMenuItem;
103    
104        private JMenuItem formattedCopyMenuItem;
105    
106        private JMenuItem pasteMenuItem;
107    
108        private JMenuItem aboutMenuItem;
109    
110        private JMenuItem openMenuItem;
111    
112        private JDialog aboutDialog;
113    
114        private JSplitPane splitPane;
115    
116        private JScrollPane scrollPane;
117    
118        private JScrollPane scrollPane2;
119    
120        private XMLTextEditor xmlEditor;
121    
122        private JMathComponent mathComponent;
123    
124        private BiTree biTree;
125    
126        private JMenu viewMenu;
127    
128        private JMenuItem refreshMenuItem;
129    
130        private JMenuItem biggerMenuItem;
131    
132        private JMenuItem smallerMenuItem;
133    
134        private JMenuItem exportMenuItem;
135    
136        private JCheckBoxMenuItem aliasMenuItem;
137    
138        private JCheckBoxMenuItem debugMenuItem;
139    
140        // ==================================================
141        // context menu elements
142        // ==================================================
143    
144        private JPopupMenu contextPopupMenu;
145    
146        private JMenu insertMenu;
147    
148        private JMenu greekMenu;
149    
150        private JMenu logicsMenu;
151    
152        private JMenu symbolsMenu;
153    
154        private JMenuItem c_refreshMenuItem;
155    
156        private JMenuItem tableMenuItem;
157    
158        private JMenuItem polynomMenuItem;
159    
160        private JMenuItem orMenuItem;
161    
162        private JMenuItem andMenuItem;
163    
164        private JMenuItem notMenuItem;
165    
166        private JMenuItem alphaMenuItem;
167    
168        private JMenuItem betaMenuItem;
169    
170        private JMenuItem gammaMenuItem;
171    
172        private JMenuItem deltaMenuItem;
173    
174        private JMenuItem omegaMenuItem;
175    
176        private JMenuItem existsMenuItem;
177    
178        private JMenuItem forallMenuItem;
179    
180        /**
181         * This is the default constructor.
182         */
183        public MainFrame() {
184            super();
185            this.initialize();
186        }
187    
188        @Override
189        public void updateCursorPosition(final Node node) {
190            final TextPosition result = this.biTree.searchNode(node);
191            if (result != null
192                    && result.getTotalOffset() < this.getXMLEditor().getText()
193                            .length()) {
194                this.getXMLEditor().requestFocusInWindow();
195                this.getXMLEditor().setCaretPosition(result.getTotalOffset());
196                this.getXMLEditor().setSelectionStart(result.getTotalOffset());
197                this.getXMLEditor().setSelectionEnd(
198                        result.getTotalOffset() + result.getLength());
199            }
200        }
201    
202        /**
203         * This method initializes this
204         * 
205         * @return void
206         */
207        private void initialize() {
208            this.setJMenuBar(this.getJJMenuBar());
209            this.setSize(MainFrame.DEFAULT_WIDTH, MainFrame.DEFAULT_HEIGHT);
210            this.setContentPane(this.getJContentPane());
211            this.setTitle(Messages.getString("MathViewer.windowTitle")); //$NON-NLS-1$
212            this.setLocationByPlatform(true);
213        }
214    
215        /**
216         * This method initializes jContentPane
217         * 
218         * @return javax.swing.JPanel
219         */
220        private JPanel getJContentPane() {
221            if (this.jContentPane == null) {
222                this.jContentPane = new JPanel();
223                this.jContentPane.setLayout(new BorderLayout());
224                this.jContentPane.add(this.getSplitPane(), BorderLayout.CENTER);
225            }
226            return this.jContentPane;
227        }
228    
229        /**
230         * This method initializes jJMenuBar
231         * 
232         * @return javax.swing.JMenuBar
233         */
234        private JMenuBar getJJMenuBar() {
235            if (this.jJMenuBar == null) {
236                this.jJMenuBar = new JMenuBar();
237                this.jJMenuBar.add(this.getFileMenu());
238                this.jJMenuBar.add(this.getEditMenu());
239                this.jJMenuBar.add(this.getViewMenu());
240                if (!MathViewer.OSX) {
241                    // This will need to be changed once the Help menu contains
242                    // more that just the About item.
243                    this.jJMenuBar.add(this.getHelpMenu());
244                }
245            }
246            return this.jJMenuBar;
247        }
248    
249        /**
250         * This method initializes jMenu
251         * 
252         * @return javax.swing.JMenu
253         */
254        private JMenu getFileMenu() {
255            if (this.fileMenu == null) {
256                this.fileMenu = new JMenu();
257                this.fileMenu.setText(Messages.getString("MathViewer.FileMenu")); //$NON-NLS-1$
258                this.fileMenu.add(this.getOpenMenuItem());
259                this.fileMenu.add(this.getExportMenuItem());
260                if (!MathViewer.OSX) {
261                    this.fileMenu.add(this.getExitMenuItem());
262                }
263            }
264            return this.fileMenu;
265        }
266    
267        /**
268         * This method initializes jMenu
269         * 
270         * @return javax.swing.JMenu
271         */
272        private JMenu getEditMenu() {
273            if (this.editMenu == null) {
274                this.editMenu = new JMenu();
275                this.editMenu.setText(Messages.getString("MathViewer.EditMenu")); //$NON-NLS-1$
276                this.editMenu.add(this.getUnformattedCopyMenuItem());
277                this.editMenu.add(this.getFormattedCopyMenuItem());
278                this.editMenu.add(this.getPasteMenuItem());
279            }
280            return this.editMenu;
281        }
282    
283        /**
284         * This method initializes jMenu
285         * 
286         * @return javax.swing.JMenu
287         */
288        private JMenu getHelpMenu() {
289            if (this.helpMenu == null) {
290                this.helpMenu = new JMenu();
291                this.helpMenu.setText(Messages.getString("MathViewer.helpMenu")); //$NON-NLS-1$
292                // If there are more items, please modify getJJMenuBar to always
293                // display the help menu and this function to not display about on
294                // OS X
295                this.helpMenu.add(this.getAboutMenuItem());
296            }
297            return this.helpMenu;
298        }
299    
300        /**
301         * This method initializes jMenuItem
302         * 
303         * @return javax.swing.JMenuItem
304         */
305        private JMenuItem getExitMenuItem() {
306            if (this.exitMenuItem == null) {
307                this.exitMenuItem = new JMenuItem();
308                this.exitMenuItem.setText(Messages.getString("MathViewer.exit")); //$NON-NLS-1$
309                this.exitMenuItem.setAccelerator(KeyStroke.getKeyStroke(
310                        KeyEvent.VK_Q, Toolkit.getDefaultToolkit()
311                                .getMenuShortcutKeyMask(), true));
312    
313                this.exitMenuItem.addActionListener(new ActionListener() {
314                    public void actionPerformed(final ActionEvent e) {
315                        System.exit(0);
316                    }
317                });
318            }
319            return this.exitMenuItem;
320        }
321    
322        private JMenuItem getUnformattedCopyMenuItem() {
323            if (this.unformattedCopyMenuItem == null) {
324                this.unformattedCopyMenuItem = new JMenuItem();
325                this.unformattedCopyMenuItem.setText(Messages
326                        .getString("MathViewer.unformattedCopyFull")); //$NON-NLS-1$
327                // this.unformattedCopyMenuItem.setAccelerator(KeyStroke
328                // .getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit()
329                // .getMenuShortcutKeyMask(), true));
330    
331                this.unformattedCopyMenuItem
332                        .addActionListener(new ActionListener() {
333                            public void actionPerformed(final ActionEvent e) {
334                                MainFrame.this.copyFullToClipboard(false);
335                            }
336                        });
337            }
338            return this.unformattedCopyMenuItem;
339        }
340    
341        private JMenuItem getFormattedCopyMenuItem() {
342            if (this.formattedCopyMenuItem == null) {
343                this.formattedCopyMenuItem = new JMenuItem();
344                this.formattedCopyMenuItem.setText(Messages
345                        .getString("MathViewer.formattedCopyFull")); //$NON-NLS-1$
346                // this.formattedCopyMenuItem.setAccelerator(KeyStroke.getKeyStroke(
347                // KeyEvent.VK_C, Toolkit.getDefaultToolkit()
348                // .getMenuShortcutKeyMask()
349                // | InputEvent.SHIFT_DOWN_MASK, true));
350    
351                this.formattedCopyMenuItem.addActionListener(new ActionListener() {
352                    public void actionPerformed(final ActionEvent e) {
353                        MainFrame.this.copyFullToClipboard(true);
354                    }
355                });
356            }
357            return this.formattedCopyMenuItem;
358        }
359    
360        /**
361         * This method initializes jMenuItem
362         * 
363         * @return javax.swing.JMenuItem
364         */
365        private JMenuItem getPasteMenuItem() {
366            if (this.pasteMenuItem == null) {
367                this.pasteMenuItem = new JMenuItem();
368                this.pasteMenuItem.setText(Messages
369                        .getString("MathViewer.pasteFull")); //$NON-NLS-1$
370                // this.pasteMenuItem.setAccelerator(KeyStroke.getKeyStroke(
371                // KeyEvent.VK_V, Toolkit.getDefaultToolkit()
372                // .getMenuShortcutKeyMask(), true));
373    
374                this.pasteMenuItem.addActionListener(new ActionListener() {
375                    public void actionPerformed(final ActionEvent e) {
376                        MainFrame.this.pasteFullFromClipboard();
377                    }
378                });
379            }
380            return this.pasteMenuItem;
381        }
382    
383        /**
384         * This method initializes jMenuItem
385         * 
386         * @return javax.swing.JMenuItem
387         */
388        private JMenuItem getAboutMenuItem() {
389            if (this.aboutMenuItem == null) {
390                this.aboutMenuItem = new JMenuItem();
391                this.aboutMenuItem.setText(Messages
392                        .getString("MathViewer.aboutMenuItem")); //$NON-NLS-1$
393                this.aboutMenuItem.addActionListener(new ActionListener() {
394                    public void actionPerformed(final ActionEvent e) {
395                        MainFrame.this.displayAbout();
396                    }
397                });
398            }
399            return this.aboutMenuItem;
400        }
401    
402        /**
403         * Display the about dialog.
404         */
405        public void displayAbout() {
406            final JDialog aDialog = MainFrame.this.getAboutDialog();
407            aDialog.pack();
408            final Point loc = MainFrame.this.getLocation();
409            loc.translate((MainFrame.this.getWidth() - aDialog.getWidth()) / 2, 0);
410            aDialog.setLocation(loc);
411            aDialog.setVisible(true);
412    
413        }
414    
415        /**
416         * This method initializes aboutDialog
417         * 
418         * @return javax.swing.JDialog
419         */
420        private JDialog getAboutDialog() {
421            if (this.aboutDialog == null) {
422                this.aboutDialog = new AboutDialog(this);
423            }
424            return this.aboutDialog;
425        }
426    
427        /**
428         * This method initializes jMenuItem
429         * 
430         * @return javax.swing.JMenuItem
431         */
432        private JMenuItem getOpenMenuItem() {
433            if (this.openMenuItem == null) {
434                this.openMenuItem = new JMenuItem();
435                this.openMenuItem.setText(Messages.getString("MathViewer.open")); //$NON-NLS-1$
436                this.openMenuItem.setAccelerator(KeyStroke.getKeyStroke(
437                        KeyEvent.VK_O, Toolkit.getDefaultToolkit()
438                                .getMenuShortcutKeyMask(), true));
439                this.openMenuItem
440                        .addActionListener(new java.awt.event.ActionListener() {
441                            public void actionPerformed(
442                                    final java.awt.event.ActionEvent e) {
443                                MainFrame.this.openFile();
444                            }
445                        });
446            }
447            return this.openMenuItem;
448        }
449    
450        /**
451         * Try to load a given file into this frame.
452         * 
453         * @param f
454         *            reference to the file.
455         */
456        public void loadFile(final File f) {
457            final Document doc = MainFrame.FILEIO.loadFile(this, f);
458            if (doc == null) {
459                this.getXMLEditor().setText("");
460            } else {
461                this.getXMLEditor().setText(
462                        MathMLSerializer.serializeDocument(doc, false, false));
463            }
464            this.updateFromTextArea();
465        }
466    
467        /**
468         * carries out the actual file-open procedure.
469         */
470        protected void openFile() {
471            final File file = MainFrame.FILEIO.selectFileToOpen(this);
472            this.loadFile(file);
473        }
474    
475        /**
476         * This method initializes splitPane
477         * 
478         * @return {@link JSplitPane}
479         */
480        private JSplitPane getSplitPane() {
481            if (this.splitPane == null) {
482                this.splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
483                        this.getScrollPane(), this.getScrollPane2());
484                this.splitPane.setOneTouchExpandable(true);
485                this.splitPane.setResizeWeight(1.0);
486            }
487            return this.splitPane;
488        }
489    
490        private void setText(final String text) {
491            this.xmlEditor.setText(text);
492            try {
493                this.biTree = SAXBiParser.getInstance().parse(text);
494            } catch (NonIncrementalElementException e) {
495                this.biTree = null;
496                this.getMathComponent().setContent(text);
497            }
498            if (this.biTree != null) {
499                this.biTree.createDOMTree();
500                this.mathComponent.setDocument(this.biTree.getDocument());
501            }
502        }
503    
504        /**
505         * This method initializes xmlEditor
506         * 
507         * @return {@link XMLTextEditor}
508         */
509        private XMLTextEditor getXMLEditor() {
510            if (this.xmlEditor == null) {
511                this.xmlEditor = new XMLTextEditor();
512                this.xmlEditor.setEditorKit(new XMLEditorKit(new XMLContext()));
513                /*
514                 * this.xmlEditor.setText("<?xml version='1.0'?>" + Helper.nl() +
515                 * "<math xmlns='http://www.w3.org/1998/Math/MathML'>" + Helper.nl()
516                 * + "</math>");
517                 */
518                this.setText("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
519                        + Helper.nl()
520                        // DOCTYPE for W3C compliance obviously not
521                        // supported
522                        // +
523                        // "<!DOCTYPE math PUBLIC -//W3C//DTD MathML 2.0//EN' "
524                        // +
525                        // "'http://www.w3.org/Math/DTD/mathml2/mathml2.dtd'>"
526                        // + Helper.nl()
527                        + "<math xmlns='http://www.w3.org/1998/Math/MathML'>"
528                        + Helper.nl() + "<mrow>" + Helper.nl() + "<mi>a</mi>"
529                        + Helper.nl() + "<msup><mi>x</mi><mn>2</mn></msup>"
530                        + Helper.nl() + "<mo>+</mo><mi>b</mi>" + Helper.nl()
531                        + "<mi>x</mi><mo>+</mo><mi>c</mi>" + Helper.nl()
532                        + "<mo>=</mo><mo>0</mo>" + Helper.nl() + "</mrow>"
533                        + Helper.nl() + "</math>");
534    
535                this.xmlEditor.setEditable(true);
536                this.xmlEditor.setComponentPopupMenu(this.getContextPopupMenu());
537                this.xmlEditor.getDocument().addDocumentListener(
538                        new DocumentListener() {
539                            public void changedUpdate(
540                                    final DocumentEvent documentevent) {
541                                MainFrame.this.updateFromTextArea(documentevent);
542                            }
543    
544                            public void insertUpdate(
545                                    final DocumentEvent documentevent) {
546                                MainFrame.this.updateFromTextArea(documentevent);
547                            }
548    
549                            public void removeUpdate(
550                                    final DocumentEvent documentevent) {
551                                MainFrame.this.updateFromTextArea(documentevent);
552                            }
553                        });
554                // this.updateFromTextArea();
555            }
556    
557            return this.xmlEditor;
558        }
559    
560        private String normalize(final String text) {
561            // workaround for some problems with OS dependency
562            // pane.getDocument().putProperty(DefaultEditorKit.EndOfLineStringProperty,
563            // "\n");
564            return text.replace("\r\n", "\n");
565        }
566    
567        /**
568         * Set the content from a String containing the MathML content.
569         * 
570         * @param text
571         *            the content to set.
572         * @param documentEvent
573         *            documentEvent which triggered the change.
574         */
575        private void setContent(final DocumentEvent documentEvent, final String text) {
576            DocumentEvent.EventType type;
577            if (this.biTree == null || this.biTree.getRoot() == null) {
578                this.getMathComponent().setContent(text);
579            } else {
580    
581                type = documentEvent.getType();
582    
583                try {
584                    if (type == DocumentEvent.EventType.INSERT) {
585                        this.biTree.insert(documentEvent.getOffset(),
586                                documentEvent.getLength(), text);
587    
588                    } else if (type == DocumentEvent.EventType.REMOVE) {
589                        this.biTree.remove(documentEvent.getOffset(),
590                                documentEvent.getLength(), text);
591    
592                    } else {
593                        // Other types of events are unsupported.
594                        this.getMathComponent().setContent(text);
595                    }
596                } catch (final ReparseException ex) {
597                    this.getMathComponent().setContent(text);
598                } catch (NonIncrementalElementException e) {
599                    this.biTree = null;
600                    this.getMathComponent().setContent(text);
601                }
602            }
603            this.getMathComponent().revalidate();
604            this.getMathComponent().repaint();
605        }
606    
607        private void updateFromTextArea(final DocumentEvent documentevent) {
608            try {
609                final String txt = this.normalize(this.getXMLEditor().getText());
610                this.setContent(documentevent, txt);
611    
612                // CHECKSTYLE:OFF
613                // in this case, we want to explicitly provide catch-all error
614                // handling.
615            } catch (final RuntimeException e) {
616                // CHECKSTYLE:ON
617            }
618        }
619    
620        private void updateFromTextArea() {
621            try {
622                final String txt = this.normalize(this.getXMLEditor().getText());
623                this.setText(txt);
624    
625                // CHECKSTYLE:OFF
626                // in this case, we want to explicitly provide catch-all error
627                // handling.
628            } catch (final RuntimeException e) {
629                // CHECKSTYLE:ON
630            }
631        }
632    
633        /**
634         * This method initializes scrollPane
635         * 
636         * @return javax.swing.JScrollPane
637         */
638        private JScrollPane getScrollPane() {
639            if (this.scrollPane == null) {
640                this.scrollPane = new JScrollPane();
641                this.scrollPane.setViewportView(this.getMathComponent());
642    
643                if (MathViewer.OSX) {
644                    this.scrollPane
645                            .setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
646                    this.scrollPane
647                            .setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
648                }
649            }
650            return this.scrollPane;
651        }
652    
653        /**
654         * This method initializes scrollPane2
655         * 
656         * @return javax.swing.JScrollPane
657         */
658        private JScrollPane getScrollPane2() {
659            if (this.scrollPane2 == null) {
660                this.scrollPane2 = new JScrollPane();
661                this.scrollPane2.setViewportView(this.getXMLEditor());
662    
663                if (MathViewer.OSX) {
664                    this.scrollPane2
665                            .setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
666                    this.scrollPane2
667                            .setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
668                }
669            }
670            return this.scrollPane2;
671        }
672    
673        /**
674         * This method initializes mathComponent.
675         * 
676         * @return net.sourceforge.jeuclid.swing.JMathComponent
677         */
678        public JMathComponent getMathComponent() {
679            if (this.mathComponent == null) {
680                this.mathComponent = new JMathComponent(this);
681                this.mathComponent
682                        .setContent("<math><mtext>" //$NON-NLS-1$
683                                + Messages.getString("MathViewer.noFileLoaded") + "</mtext></math>"); //$NON-NLS-1$ //$NON-NLS-2$
684                this.mathComponent.setFocusable(true);
685            }
686            return this.mathComponent;
687        }
688    
689        /**
690         * This method initializes viewMenu
691         * 
692         * @return javax.swing.JMenu
693         */
694        private JMenu getViewMenu() {
695            if (this.viewMenu == null) {
696                this.viewMenu = new JMenu();
697                this.viewMenu.setText(Messages.getString("MathViewer.viewMenu")); //$NON-NLS-1$
698                this.viewMenu.add(this.getRefreshMenuItem());
699                this.viewMenu.add(this.getBiggerMenuItem());
700                this.viewMenu.add(this.getSmallerMenuItem());
701                this.viewMenu.add(this.getAliasMenuItem());
702                this.viewMenu.add(this.getDebugMenuItem());
703                if (!MathViewer.OSX) {
704                    this.viewMenu.add(this.getViewModifyParams());
705                }
706            }
707            return this.viewMenu;
708        }
709    
710        private JMenuItem getViewModifyParams() {
711            final JMenuItem mi = new JMenuItem(
712                    Messages.getString("MathViewer.viewModifyParams"));
713            mi.addActionListener(new ActionListener() {
714                public void actionPerformed(final ActionEvent e) {
715                    MainFrame.this.displaySettings();
716                }
717            });
718            return mi;
719        }
720    
721        /**
722         * Display the settings dialog.
723         */
724        public void displaySettings() {
725            new ParametersDialog(MainFrame.this).setVisible(true);
726            MainFrame.this.debugMenuItem
727                    .setSelected(((Boolean) MainFrame.this.mathComponent
728                            .getParameters().getParameter(Parameter.DEBUG))
729                            .booleanValue());
730            MainFrame.this.aliasMenuItem
731                    .setSelected(((Boolean) MainFrame.this.mathComponent
732                            .getParameters().getParameter(Parameter.ANTIALIAS))
733                            .booleanValue());
734    
735        }
736    
737        /**
738         * This method initializes refreshMenuItem
739         * 
740         * @return javax.swing.JMenuItem
741         */
742        private JMenuItem getRefreshMenuItem() {
743            if (this.refreshMenuItem == null) {
744                this.refreshMenuItem = new JMenuItem();
745                this.refreshMenuItem.setText(Messages
746                        .getString("MathViewer.textRefresh"));
747                this.refreshMenuItem.setAccelerator(KeyStroke.getKeyStroke(
748                        KeyEvent.VK_Y, Toolkit.getDefaultToolkit()
749                                .getMenuShortcutKeyMask(), true));
750                this.refreshMenuItem
751                        .addActionListener(new java.awt.event.ActionListener() {
752                            public void actionPerformed(
753                                    final java.awt.event.ActionEvent e) {
754                                MainFrame.this.updateFromTextArea();
755                            }
756                        });
757            }
758            return this.refreshMenuItem;
759        }
760    
761        /**
762         * This method initializes biggerMenuItem
763         * 
764         * @return javax.swing.JMenuItem
765         */
766        private JMenuItem getBiggerMenuItem() {
767            if (this.biggerMenuItem == null) {
768                this.biggerMenuItem = new JMenuItem();
769                this.biggerMenuItem.setText(Messages
770                        .getString("MathViewer.textBigger")); //$NON-NLS-1$
771                this.biggerMenuItem.setAccelerator(KeyStroke.getKeyStroke(
772                /*
773                 * KeyEvent.VK_ADD, Toolkit.getDefaultToolkit()
774                 * .getMenuShortcutKeyMask(), true));
775                 */
776                KeyEvent.VK_PLUS, Toolkit.getDefaultToolkit()
777                        .getMenuShortcutKeyMask(), true));
778                this.biggerMenuItem
779                        .addActionListener(new java.awt.event.ActionListener() {
780                            public void actionPerformed(
781                                    final java.awt.event.ActionEvent e) {
782                                final JMathComponent jmc = MainFrame.this
783                                        .getMathComponent();
784                                jmc.setFontSize(jmc.getFontSize()
785                                        * MainFrame.FONT_SIZE_MULTIPLICATOR);
786                            }
787                        });
788            }
789            return this.biggerMenuItem;
790        }
791    
792        /**
793         * This method initializes smallerMenuItem
794         * 
795         * @return javax.swing.JMenuItem
796         */
797        private JMenuItem getSmallerMenuItem() {
798            if (this.smallerMenuItem == null) {
799                this.smallerMenuItem = new JMenuItem();
800                this.smallerMenuItem.setText(Messages
801                        .getString("MathViewer.textSmaller")); //$NON-NLS-1$
802                this.smallerMenuItem.setAccelerator(KeyStroke.getKeyStroke(
803                /*
804                 * KeyEvent.VK_SUBTRACT, Toolkit.getDefaultToolkit()
805                 * .getMenuShortcutKeyMask(), true));
806                 */
807                KeyEvent.VK_MINUS, Toolkit.getDefaultToolkit()
808                        .getMenuShortcutKeyMask(), true));
809    
810                this.smallerMenuItem
811                        .addActionListener(new java.awt.event.ActionListener() {
812                            public void actionPerformed(
813                                    final java.awt.event.ActionEvent e) {
814                                final JMathComponent jmc = MainFrame.this
815                                        .getMathComponent();
816                                jmc.setFontSize(jmc.getFontSize()
817                                        / MainFrame.FONT_SIZE_MULTIPLICATOR);
818                            }
819                        });
820            }
821            return this.smallerMenuItem;
822        }
823    
824        /**
825         * This method initializes exportMenuItem.
826         * 
827         * @return javax.swing.JMenuItem
828         */
829        private JMenuItem getExportMenuItem() {
830            if (this.exportMenuItem == null) {
831                this.exportMenuItem = new JMenuItem();
832                this.exportMenuItem
833                        .setText(Messages.getString("MathViewer.export")); //$NON-NLS-1$
834                this.exportMenuItem.setAccelerator(KeyStroke.getKeyStroke(
835                        KeyEvent.VK_S, Toolkit.getDefaultToolkit()
836                                .getMenuShortcutKeyMask(), true));
837                this.exportMenuItem
838                        .addActionListener(new java.awt.event.ActionListener() {
839                            public void actionPerformed(
840                                    final java.awt.event.ActionEvent e) {
841                                MainFrame.this.exportFile();
842                            }
843                        });
844            }
845            return this.exportMenuItem;
846        }
847    
848        /**
849         * Carries out the actual export File operation.
850         */
851        protected void exportFile() {
852            MainFrame.FILEIO.saveDocument(this, this.getMathComponent()
853                    .getDocument(), this.getMathComponent().getParameters());
854        }
855    
856        /**
857         * This method initializes aliasMenuItem
858         * 
859         * @return javax.swing.JCheckBoxMenuItem
860         */
861        private JCheckBoxMenuItem getAliasMenuItem() {
862            if (this.aliasMenuItem == null) {
863                this.aliasMenuItem = new JCheckBoxMenuItem();
864                this.aliasMenuItem.setText(Messages.getString("MathViewer.alias")); //$NON-NLS-1$
865                this.aliasMenuItem.setSelected((Boolean) LayoutContextImpl
866                        .getDefaultLayoutContext()
867                        .getParameter(Parameter.ANTIALIAS));
868                this.aliasMenuItem
869                        .addItemListener(new java.awt.event.ItemListener() {
870                            public void itemStateChanged(
871                                    final java.awt.event.ItemEvent e) {
872                                MainFrame.this.getMathComponent().setParameter(
873                                        Parameter.ANTIALIAS,
874                                        MainFrame.this.aliasMenuItem.isSelected());
875                            }
876                        });
877            }
878            return this.aliasMenuItem;
879        }
880    
881        /**
882         * This method initializes debugMenuItem
883         * 
884         * @return javax.swing.JCheckBoxMenuItem
885         */
886        private JCheckBoxMenuItem getDebugMenuItem() {
887            if (this.debugMenuItem == null) {
888                this.debugMenuItem = new JCheckBoxMenuItem();
889                this.debugMenuItem.setText(Messages.getString("MathViewer.debug")); //$NON-NLS-1$
890                this.debugMenuItem.setSelected((Boolean) LayoutContextImpl
891                        .getDefaultLayoutContext().getParameter(Parameter.DEBUG));
892                this.debugMenuItem
893                        .addItemListener(new java.awt.event.ItemListener() {
894                            public void itemStateChanged(
895                                    final java.awt.event.ItemEvent e) {
896                                MainFrame.this.getMathComponent().setParameter(
897                                        Parameter.DEBUG,
898                                        MainFrame.this.debugMenuItem.isSelected());
899                            }
900                        });
901            }
902            return this.debugMenuItem;
903        }
904    
905        private void pasteFullFromClipboard() {
906            final Transferable content = Toolkit.getDefaultToolkit()
907                    .getSystemClipboard().getContents(null);
908            if (content != null
909                    && content.isDataFlavorSupported(DataFlavor.stringFlavor)) {
910                try {
911                    final String newContent = (String) content
912                            .getTransferData(DataFlavor.stringFlavor);
913                    this.getMathComponent().setContent(newContent);
914                    this.getXMLEditor().setText(newContent);
915                    // CHECKSTYLE:OFF
916                    // in this case, we want to explicitly provide catch-all error
917                    // handling.
918                } catch (final Exception e) {
919                    // CHECKSTYLE:ON
920                    JOptionPane.showMessageDialog(
921                            this,
922                            new String[] {
923                                    Messages.getString("MathViewer.pasteFailure"),
924                                    e.toString(), },
925                            Messages.getString("MathViewer.error"),
926                            JOptionPane.ERROR_MESSAGE);
927                }
928            }
929        }
930    
931        private void copyFullToClipboard(final boolean formatted) {
932            Toolkit.getDefaultToolkit()
933                    .getSystemClipboard()
934                    .setContents(
935                            new StringSelection(MathMLSerializer.serializeDocument(
936                                    this.mathComponent.getDocument(), false,
937                                    formatted)), null);
938        }
939    
940        private JMenuItem getCRefreshMenuItem() {
941            if (this.c_refreshMenuItem == null) {
942                this.c_refreshMenuItem = new JMenuItem();
943                this.c_refreshMenuItem.setText(Messages
944                        .getString("MathViewer.cRefreshMenuItem")); //$NON-NLS-1$
945                this.c_refreshMenuItem.setAccelerator(KeyStroke.getKeyStroke(
946                        KeyEvent.VK_F10, Toolkit.getDefaultToolkit()
947                                .getMenuShortcutKeyMask(), true));
948                this.c_refreshMenuItem.addActionListener(new ActionListener() {
949                    public void actionPerformed(final ActionEvent e) {
950                        MainFrame.this.updateFromTextArea();
951                    }
952                });
953            }
954            return this.c_refreshMenuItem;
955        }
956    
957        private JMenuItem getTableMenuItem() {
958            if (this.tableMenuItem == null) {
959                this.tableMenuItem = new JMenuItem();
960                this.tableMenuItem.setText(Messages
961                        .getString("MathViewer.tableMenuItem")); //$NON-NLS-1$
962                this.tableMenuItem.addActionListener(new ActionListener() {
963                    public void actionPerformed(final ActionEvent e) {
964                        final InsertTableDialog dlg = new InsertTableDialog(
965                                MainFrame.this, true);
966                        dlg.setVisible(true);
967    
968                        if (dlg.getMathMLText() != null) {
969                            MainFrame.this.insertMacro(dlg.getMathMLText());
970                        }
971                    }
972                });
973            }
974            return this.tableMenuItem;
975        }
976    
977        private JMenuItem getPolynomMenuItem() {
978            if (this.polynomMenuItem == null) {
979                this.polynomMenuItem = new JMenuItem();
980                this.polynomMenuItem.setText(Messages
981                        .getString("MathViewer.polynomMenuItem")); //$NON-NLS-1$
982                this.polynomMenuItem.addActionListener(new ActionListener() {
983                    public void actionPerformed(final ActionEvent e) {
984                        final InsertPolynomDialog dlg = new InsertPolynomDialog(
985                                MainFrame.this, true);
986                        dlg.setVisible(true);
987                        if (dlg.getMathMLText() != null) {
988                            MainFrame.this.insertMacro(dlg.getMathMLText());
989                        }
990                    }
991                });
992            }
993            return this.polynomMenuItem;
994        }
995    
996        private JMenuItem getOrMenuItem() {
997            if (this.orMenuItem == null) {
998                this.orMenuItem = new JMenuItem();
999                this.orMenuItem
1000                        .setText(Messages.getString("MathViewer.orMenuItem")); //$NON-NLS-1$
1001                this.orMenuItem.addActionListener(new ActionListener() {
1002                    public void actionPerformed(final ActionEvent e) {
1003                        MainFrame.this
1004                                .insertMacro("<apply><or/><ci>a</ci><ci>b</ci></apply>");
1005                    }
1006                });
1007            }
1008            return this.orMenuItem;
1009        }
1010    
1011        private JMenuItem getAndMenuItem() {
1012            if (this.andMenuItem == null) {
1013                this.andMenuItem = new JMenuItem();
1014                this.andMenuItem.setText(Messages
1015                        .getString("MathViewer.andMenuItem")); //$NON-NLS-1$
1016                this.andMenuItem.addActionListener(new ActionListener() {
1017                    public void actionPerformed(final ActionEvent e) {
1018                        MainFrame.this
1019                                .insertMacro("<apply><and/><ci>a</ci><ci>b</ci></apply>");
1020                    }
1021                });
1022            }
1023            return this.andMenuItem;
1024        }
1025    
1026        private JMenuItem getNotMenuItem() {
1027            if (this.notMenuItem == null) {
1028                this.notMenuItem = new JMenuItem();
1029                this.notMenuItem.setText(Messages
1030                        .getString("MathViewer.notMenuItem")); //$NON-NLS-1$
1031                this.notMenuItem.addActionListener(new ActionListener() {
1032                    public void actionPerformed(final ActionEvent e) {
1033                        MainFrame.this
1034                                .insertMacro("<apply><not/><ci>a</ci></apply>");
1035                    }
1036                });
1037            }
1038            return this.notMenuItem;
1039        }
1040    
1041        private JMenuItem getAlphaMenuItem() {
1042            if (this.alphaMenuItem == null) {
1043                this.alphaMenuItem = new JMenuItem();
1044                this.alphaMenuItem.setText(Messages
1045                        .getString("MathViewer.alphaMenuItem")); //$NON-NLS-1$
1046                this.alphaMenuItem.addActionListener(new ActionListener() {
1047                    public void actionPerformed(final ActionEvent e) {
1048                        MainFrame.this.insertMacro("<mi>&#x003b1;</mi>");
1049                    }
1050                });
1051            }
1052            return this.alphaMenuItem;
1053        }
1054    
1055        private JMenuItem getBetaMenuItem() {
1056            if (this.betaMenuItem == null) {
1057                this.betaMenuItem = new JMenuItem();
1058                this.betaMenuItem.setText(Messages
1059                        .getString("MathViewer.betaMenuItem")); //$NON-NLS-1$
1060                this.betaMenuItem.addActionListener(new ActionListener() {
1061                    public void actionPerformed(final ActionEvent e) {
1062                        MainFrame.this.insertMacro("<mi>&#x003b2;</mi>");
1063                    }
1064                });
1065            }
1066            return this.betaMenuItem;
1067        }
1068    
1069        private JMenuItem getGammaMenuItem() {
1070            if (this.gammaMenuItem == null) {
1071                this.gammaMenuItem = new JMenuItem();
1072                this.gammaMenuItem.setText(Messages
1073                        .getString("MathViewer.gammaMenuItem")); //$NON-NLS-1$
1074                this.gammaMenuItem.addActionListener(new ActionListener() {
1075                    public void actionPerformed(final ActionEvent e) {
1076                        MainFrame.this.insertMacro("<mi>&#x003b3;</mi>");
1077                    }
1078                });
1079            }
1080            return this.gammaMenuItem;
1081        }
1082    
1083        private JMenuItem getDeltaMenuItem() {
1084            if (this.deltaMenuItem == null) {
1085                this.deltaMenuItem = new JMenuItem();
1086                this.deltaMenuItem.setText(Messages
1087                        .getString("MathViewer.deltaMenuItem")); //$NON-NLS-1$
1088                this.deltaMenuItem.addActionListener(new ActionListener() {
1089                    public void actionPerformed(final ActionEvent e) {
1090                        MainFrame.this.insertMacro("<mi>&#x003b4;</mi>");
1091                    }
1092                });
1093            }
1094            return this.deltaMenuItem;
1095        }
1096    
1097        private JMenuItem getOmegaMenuItem() {
1098            if (this.omegaMenuItem == null) {
1099                this.omegaMenuItem = new JMenuItem();
1100                this.omegaMenuItem.setText(Messages
1101                        .getString("MathViewer.omegaMenuItem")); //$NON-NLS-1$
1102                this.omegaMenuItem.addActionListener(new ActionListener() {
1103                    public void actionPerformed(final ActionEvent e) {
1104                        MainFrame.this.insertMacro("<mi>&#x003c9;</mi>");
1105                    }
1106                });
1107            }
1108            return this.omegaMenuItem;
1109        }
1110    
1111        private JMenuItem getExistsMenuItem() {
1112            if (this.existsMenuItem == null) {
1113                this.existsMenuItem = new JMenuItem();
1114                this.existsMenuItem.setText(Messages
1115                        .getString("MathViewer.existsMenuItem")); //$NON-NLS-1$
1116                this.existsMenuItem.addActionListener(new ActionListener() {
1117                    public void actionPerformed(final ActionEvent e) {
1118                        MainFrame.this.insertMacro("<apply><exists/></apply>");
1119                    }
1120                });
1121            }
1122            return this.existsMenuItem;
1123        }
1124    
1125        private JMenuItem getForAllMenuItem() {
1126            if (this.forallMenuItem == null) {
1127                this.forallMenuItem = new JMenuItem();
1128                this.forallMenuItem.setText(Messages
1129                        .getString("MathViewer.forallMenuItem")); //$NON-NLS-1$
1130                this.forallMenuItem.addActionListener(new ActionListener() {
1131                    public void actionPerformed(final ActionEvent e) {
1132                        MainFrame.this.insertMacro("<apply><forall/></apply>");
1133                    }
1134                });
1135            }
1136            return this.forallMenuItem;
1137        }
1138    
1139        private JMenu getGreekMenu() {
1140            if (this.greekMenu == null) {
1141                this.greekMenu = new JMenu();
1142                this.greekMenu.setText(Messages.getString("MathViewer.GreekMenu")); //$NON-NLS-1$
1143                this.greekMenu.add(this.getAlphaMenuItem());
1144                this.greekMenu.add(this.getBetaMenuItem());
1145                this.greekMenu.add(this.getGammaMenuItem());
1146                this.greekMenu.add(this.getDeltaMenuItem());
1147                this.greekMenu.add(this.getOmegaMenuItem());
1148            }
1149            return this.greekMenu;
1150        }
1151    
1152        private JMenu getLogicsMenu() {
1153            if (this.logicsMenu == null) {
1154                this.logicsMenu = new JMenu();
1155                this.logicsMenu
1156                        .setText(Messages.getString("MathViewer.LogicsMenu")); //$NON-NLS-1$
1157                this.logicsMenu.add(this.getAndMenuItem());
1158                this.logicsMenu.add(this.getOrMenuItem());
1159                this.logicsMenu.add(this.getNotMenuItem());
1160            }
1161            return this.logicsMenu;
1162        }
1163    
1164        private JMenu getSymbolsMenu() {
1165            if (this.symbolsMenu == null) {
1166                this.symbolsMenu = new JMenu();
1167                this.symbolsMenu.setText(Messages
1168                        .getString("MathViewer.SymbolsMenu")); //$NON-NLS-1$
1169                this.symbolsMenu.add(this.getForAllMenuItem());
1170                this.symbolsMenu.add(this.getExistsMenuItem());
1171            }
1172            return this.symbolsMenu;
1173        }
1174    
1175        private JMenu getInsertMenu() {
1176            if (this.insertMenu == null) {
1177                this.insertMenu = new JMenu();
1178                this.insertMenu
1179                        .setText(Messages.getString("MathViewer.InsertMenu")); //$NON-NLS-1$
1180                this.insertMenu.add(this.getTableMenuItem());
1181                this.insertMenu.add(this.getPolynomMenuItem());
1182                this.insertMenu.add(this.getGreekMenu());
1183                this.insertMenu.add(this.getLogicsMenu());
1184                this.insertMenu.add(this.getSymbolsMenu());
1185            }
1186            return this.insertMenu;
1187        }
1188    
1189        private JPopupMenu getContextPopupMenu() {
1190            if (this.contextPopupMenu == null) {
1191                this.contextPopupMenu = new JPopupMenu();
1192                this.contextPopupMenu.add(this.getCRefreshMenuItem());
1193                this.contextPopupMenu.add(this.getInsertMenu());
1194            }
1195            return this.contextPopupMenu;
1196        }
1197    
1198        private void insertMacro(final String macroText) {
1199            final int pos = this.getXMLEditor().getCaretPosition();
1200            String s1 = this.getXMLEditor().getText().substring(0, pos);
1201            final String s2 = this.getXMLEditor().getText().substring(pos);
1202            s1 += macroText + s2;
1203            this.getXMLEditor().setText(s1);
1204            this.updateFromTextArea();
1205        }
1206    }