/* * EditorDialog.java * * Created on June 25, 2007, 3:49 PM */ package editor; import eug.parser.EUGFileIO; import eug.parser.ParserSettings; import eug.shared.GenericList; import eug.shared.GenericObject; import java.awt.*; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.FlavorEvent; import java.awt.datatransfer.FlavorListener; import java.awt.event.*; import java.util.Vector; import java.util.regex.Pattern; import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; import javax.swing.undo.CannotRedoException; import javax.swing.undo.CannotUndoException; import javax.swing.undo.UndoManager; /** * * @author Michael Myers * @since 0.5pre1 */ public class EditorDialog extends JDialog { /** The contents of the original file. */ private String originalContents; protected final UndoManager undo = new UndoManager(); private static final GenericObject config = EUGFileIO.load("editor_cfg.txt", eug.parser.ParserSettings.getDefaults().setPrintTimingInfo(false)); private static Font font = initFont(); private static Font initFont() { String fontName = config.getString("editor.font.name"); // String style = config.getString("editor.font.style"); int fontSize = config.getInt("editor.font.size"); return new Font(fontName, Font.PLAIN, fontSize); } private static void setStaticFont(Font newFont) { font = newFont; config.setString("editor.font.name", newFont.getFamily(), true); // config.setString("editor.font.style", font.getStyle()) config.setInt("editor.font.size", newFont.getSize()); } /** Creates new form EditorDialog */ public EditorDialog(final java.awt.Frame parent, final String name) { super(parent, false); setTitle(name); originalContents = ""; setup(); setLocationRelativeTo(parent); } /** Creates new form EditorDialog */ public EditorDialog(final java.awt.Frame parent, final String name, final String contents) { super(parent, false); setTitle(name); originalContents = contents; setup(); setLocationRelativeTo(parent); } /** Initializes the dialog. */ private void setup() { initComponents(); //{{{ Set up the text area textPane.setCaretPosition(0); initDocument(); textPane.addCaretListener(new CaretListener() { // int lastLocation = 0; public void caretUpdate(CaretEvent e) { // TODO: Highlight matching brackets // if (e.getDot() != lastLocation) { // lastLocation = e.getDot(); // } if (e.getMark() != e.getDot()) { cutMenuItem.setEnabled(true); copyMenuItem.setEnabled(true); } else { cutMenuItem.setEnabled(false); copyMenuItem.setEnabled(false); } } }); // FIXME Is there a better way to add global key bindings? // TODO Add find and replace textPane.addKeyListener(new KeyListener() { public void keyPressed(final KeyEvent e) { if (((e.getModifiers() & KeyEvent.CTRL_MASK) != 0) || (e.getModifiers() & KeyEvent.CTRL_DOWN_MASK) != 0) { if (e.getKeyCode() == KeyEvent.VK_Z) undoMenuItem.doClick(); else if (e.getKeyCode() == KeyEvent.VK_R) redoMenuItem.doClick(); else if (e.getKeyCode() == KeyEvent.VK_G) goToMenuItem.doClick(); else if (e.getKeyCode() == KeyEvent.VK_F) findMenuItem.doClick(); } } public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { } }); // DefaultInputHandler handler = (DefaultInputHandler) textArea.getInputHandler(); // handler.addKeyBinding("C+C", new CopyListener()); // handler.addKeyBinding("C+X", new CutListener()); // handler.addKeyBinding("C+V", new PasteListener()); // handler.addKeyBinding("C+Z", new UndoListener()); // handler.addKeyBinding("C+R", new RedoListener()); // handler.addKeyBinding("C+G", new GoToListener()); //}}} Toolkit.getDefaultToolkit().getSystemClipboard().addFlavorListener( new FlavorListener() { public void flavorsChanged(FlavorEvent e) { final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); if (clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor)) { pasteMenuItem.setEnabled(true); } else { pasteMenuItem.setEnabled(false); } } } ); textAreaKeyTyped(); // set the syntaxCheckLabel } private void initDocument() { textPane.getDocument().addUndoableEditListener(new UndoableEditListener() { public void undoableEditHappened(UndoableEditEvent e) { undo.addEdit(e.getEdit()); updateUndoStates(); } }); // Here I want to make it so that if a change is made, a timer (one // second or so) is started. If the timer ends before another change is // made, then validation is triggered. // I couldn't figure out how to do it, though. textPane.getDocument().addDocumentListener(new DocumentListener() { // class TimerListener implements ActionListener { // boolean changed = false; // public void actionPerformed(ActionEvent e) { // if (changed) { // System.out.println("Changed text"); // textAreaKeyTyped(); // changed = false; // } else { // System.out.println("Not changed"); // } // } // } // private final TimerListener timerListener = new TimerListener(); // private final Timer checkTimer = new Timer(500, timerListener); public void changedUpdate(DocumentEvent e) { textAreaKeyTyped(); // timerListener.changed = true; // if (!checkTimer.isRunning()) // checkTimer.start(); // checkTimer.restart(); } public void insertUpdate(DocumentEvent e) { textAreaKeyTyped(); // timerListener.changed = true; // if (!checkTimer.isRunning()) // checkTimer.start(); // checkTimer.restart(); } public void removeUpdate(DocumentEvent e) { textAreaKeyTyped(); // timerListener.changed = true; // if (!checkTimer.isRunning()) // checkTimer.start(); // checkTimer.restart(); } }); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { javax.swing.JPanel lowerPanel = new javax.swing.JPanel(); syntaxCheckLabel = new javax.swing.JLabel(); closeButton = new javax.swing.JButton(); javax.swing.JScrollPane jScrollPane1 = new javax.swing.JScrollPane(); textPane = new javax.swing.JEditorPane(); javax.swing.JMenuBar menuBar = new javax.swing.JMenuBar(); javax.swing.JMenu fileMenu = new javax.swing.JMenu(); closeMenuItem = new javax.swing.JMenuItem(); javax.swing.JMenu editMenu = new javax.swing.JMenu(); undoMenuItem = new javax.swing.JMenuItem(); redoMenuItem = new javax.swing.JMenuItem(); javax.swing.JSeparator jSeparator1 = new javax.swing.JSeparator(); cutMenuItem = new javax.swing.JMenuItem(); copyMenuItem = new javax.swing.JMenuItem(); pasteMenuItem = new javax.swing.JMenuItem(); javax.swing.JSeparator jSeparator2 = new javax.swing.JSeparator(); findMenuItem = new javax.swing.JMenuItem(); replaceMenuItem = new javax.swing.JMenuItem(); goToMenuItem = new javax.swing.JMenuItem(); javax.swing.JMenu toolsMenu = new javax.swing.JMenu(); showCountryListMenuItem = new javax.swing.JMenuItem(); showCultureListMenuItem = new javax.swing.JMenuItem(); showProvinceListMenuItem = new javax.swing.JMenuItem(); javax.swing.JMenu formatMenu = new javax.swing.JMenu(); setFontMenuItem = new javax.swing.JMenuItem(); FormListener formListener = new FormListener(); setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE); addWindowListener(formListener); lowerPanel.add(syntaxCheckLabel); closeButton.setText("Close"); closeButton.addActionListener(formListener); lowerPanel.add(closeButton); getContentPane().add(lowerPanel, java.awt.BorderLayout.SOUTH); textPane.setContentType("text/eug"); textPane.setEditorKit(new eug.syntax.EUGEditorKit()); textPane.setFont(font); textPane.setText(originalContents); textPane.setPreferredSize(new java.awt.Dimension(600, 500)); jScrollPane1.setViewportView(textPane); getContentPane().add(jScrollPane1, java.awt.BorderLayout.CENTER); fileMenu.setMnemonic('F'); fileMenu.setText("File"); closeMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Q, java.awt.event.InputEvent.CTRL_MASK)); closeMenuItem.setText("Close"); closeMenuItem.addActionListener(formListener); fileMenu.add(closeMenuItem); menuBar.add(fileMenu); editMenu.setMnemonic('E'); editMenu.setText("Edit"); undoMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Z, java.awt.event.InputEvent.CTRL_MASK)); undoMenuItem.setMnemonic('U'); undoMenuItem.setText("Undo"); undoMenuItem.setEnabled(false); undoMenuItem.addActionListener(formListener); editMenu.add(undoMenuItem); redoMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_R, java.awt.event.InputEvent.CTRL_MASK)); redoMenuItem.setMnemonic('R'); redoMenuItem.setText("Redo"); redoMenuItem.setEnabled(false); redoMenuItem.addActionListener(formListener); editMenu.add(redoMenuItem); editMenu.add(jSeparator1); cutMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_X, java.awt.event.InputEvent.CTRL_MASK)); cutMenuItem.setMnemonic('t'); cutMenuItem.setText("Cut"); cutMenuItem.setEnabled(false); cutMenuItem.addActionListener(formListener); editMenu.add(cutMenuItem); copyMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_C, java.awt.event.InputEvent.CTRL_MASK)); copyMenuItem.setMnemonic('C'); copyMenuItem.setText("Copy"); copyMenuItem.setEnabled(false); copyMenuItem.addActionListener(formListener); editMenu.add(copyMenuItem); pasteMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_V, java.awt.event.InputEvent.CTRL_MASK)); pasteMenuItem.setMnemonic('P'); pasteMenuItem.setText("Paste"); pasteMenuItem.setEnabled(Toolkit.getDefaultToolkit().getSystemClipboard().isDataFlavorAvailable(DataFlavor.stringFlavor)); pasteMenuItem.addActionListener(formListener); editMenu.add(pasteMenuItem); editMenu.add(jSeparator2); findMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F, java.awt.event.InputEvent.CTRL_MASK)); findMenuItem.setMnemonic('F'); findMenuItem.setText("Find..."); findMenuItem.addActionListener(formListener); editMenu.add(findMenuItem); replaceMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_H, java.awt.event.InputEvent.CTRL_MASK)); replaceMenuItem.setMnemonic('R'); replaceMenuItem.setText("Replace..."); replaceMenuItem.setEnabled(false); editMenu.add(replaceMenuItem); goToMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_G, java.awt.event.InputEvent.CTRL_MASK)); goToMenuItem.setMnemonic('G'); goToMenuItem.setText("Go to..."); goToMenuItem.addActionListener(formListener); editMenu.add(goToMenuItem); menuBar.add(editMenu); toolsMenu.setText("Tools"); showCountryListMenuItem.setText("Show countries..."); showCountryListMenuItem.addActionListener(formListener); toolsMenu.add(showCountryListMenuItem); showCultureListMenuItem.setText("Show cultures..."); showCultureListMenuItem.addActionListener(formListener); toolsMenu.add(showCultureListMenuItem); showProvinceListMenuItem.setText("Show provinces..."); showProvinceListMenuItem.addActionListener(formListener); toolsMenu.add(showProvinceListMenuItem); menuBar.add(toolsMenu); formatMenu.setText("Format"); setFontMenuItem.setText("Set font..."); setFontMenuItem.addActionListener(formListener); formatMenu.add(setFontMenuItem); menuBar.add(formatMenu); setJMenuBar(menuBar); pack(); } // Code for dispatching events from components to event handlers. private class FormListener implements java.awt.event.ActionListener, java.awt.event.WindowListener { FormListener() {} public void actionPerformed(java.awt.event.ActionEvent evt) { if (evt.getSource() == closeButton) { EditorDialog.this.closeButtonActionPerformed(evt); } else if (evt.getSource() == closeMenuItem) { EditorDialog.this.closeMenuItemActionPerformed(evt); } else if (evt.getSource() == undoMenuItem) { EditorDialog.this.undoMenuItemActionPerformed(evt); } else if (evt.getSource() == redoMenuItem) { EditorDialog.this.redoMenuItemActionPerformed(evt); } else if (evt.getSource() == cutMenuItem) { EditorDialog.this.cutMenuItemActionPerformed(evt); } else if (evt.getSource() == copyMenuItem) { EditorDialog.this.copyMenuItemActionPerformed(evt); } else if (evt.getSource() == pasteMenuItem) { EditorDialog.this.pasteMenuItemActionPerformed(evt); } else if (evt.getSource() == findMenuItem) { EditorDialog.this.findMenuItemActionPerformed(evt); } else if (evt.getSource() == goToMenuItem) { EditorDialog.this.goToMenuItemActionPerformed(evt); } else if (evt.getSource() == showCountryListMenuItem) { EditorDialog.this.showCountryListMenuItemActionPerformed(evt); } else if (evt.getSource() == showCultureListMenuItem) { EditorDialog.this.showCultureListMenuItemActionPerformed(evt); } else if (evt.getSource() == showProvinceListMenuItem) { EditorDialog.this.showProvinceListMenuItemActionPerformed(evt); } else if (evt.getSource() == setFontMenuItem) { EditorDialog.this.setFontMenuItemActionPerformed(evt); } } public void windowActivated(java.awt.event.WindowEvent evt) { } public void windowClosed(java.awt.event.WindowEvent evt) { } public void windowClosing(java.awt.event.WindowEvent evt) { if (evt.getSource() == EditorDialog.this) { EditorDialog.this.formWindowClosing(evt); } } public void windowDeactivated(java.awt.event.WindowEvent evt) { } public void windowDeiconified(java.awt.event.WindowEvent evt) { } public void windowIconified(java.awt.event.WindowEvent evt) { } public void windowOpened(java.awt.event.WindowEvent evt) { } }// </editor-fold>//GEN-END:initComponents private void findMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_findMenuItemActionPerformed new FindDialog().setVisible(true); }//GEN-LAST:event_findMenuItemActionPerformed private void showProvinceListMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showProvinceListMenuItemActionPerformed ProvinceListDialog.showDialog((java.awt.Frame) getOwner()); //new ProvinceListDialog((java.awt.Frame) getOwner()).setVisible(true); }//GEN-LAST:event_showProvinceListMenuItemActionPerformed private void showCultureListMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showCultureListMenuItemActionPerformed CultureListDialog.showDialog((java.awt.Frame) getOwner()); //new CultureListDialog((java.awt.Frame) getOwner()).setVisible(true); }//GEN-LAST:event_showCultureListMenuItemActionPerformed private void showCountryListMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showCountryListMenuItemActionPerformed CountryListDialog.showDialog((java.awt.Frame) getOwner()); //new CountryListDialog((java.awt.Frame) getOwner()).setVisible(true); }//GEN-LAST:event_showCountryListMenuItemActionPerformed /* private void reformatMenuItemActionPerformed(java.awt.event.ActionEvent evt) { final GenericObject obj = EUGFileIO.loadFromString(textPane.getText(), false); if (obj == null) JOptionPane.showMessageDialog(this, "Can't reformat when there are syntax errors.", "Error", JOptionPane.ERROR_MESSAGE); else textPane.setText(obj.toString()); } */ private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed close(); }//GEN-LAST:event_closeButtonActionPerformed private void setFontMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_setFontMenuItemActionPerformed Font f = textPane.getFont(); FontSelectorDialog fsd = new FontSelectorDialog(this, f.getFamily(), f.getStyle(), f.getSize()); fsd.setVisible(true); if (fsd.closedOK) { setStaticFont(fsd.getSelectedFont()); textPane.setFont(fsd.getSelectedFont()); } }//GEN-LAST:event_setFontMenuItemActionPerformed private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosing close(); }//GEN-LAST:event_formWindowClosing private void goToMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_goToMenuItemActionPerformed int lineCount = getLineCount(); int caretLine = getCaretLine(); String response = JOptionPane.showInputDialog(this, "Go to line (0 - " + (lineCount-1) + "): ", caretLine); if (response == null || response.length() == 0) return; textPane.setCaretPosition(getLineStartOffset(Integer.parseInt(response))); }//GEN-LAST:event_goToMenuItemActionPerformed private void pasteMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pasteMenuItemActionPerformed paste(); }//GEN-LAST:event_pasteMenuItemActionPerformed private void copyMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_copyMenuItemActionPerformed copy(); }//GEN-LAST:event_copyMenuItemActionPerformed private void cutMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cutMenuItemActionPerformed cut(); }//GEN-LAST:event_cutMenuItemActionPerformed private void redoMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_redoMenuItemActionPerformed redo(); }//GEN-LAST:event_redoMenuItemActionPerformed private void undoMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_undoMenuItemActionPerformed undo(); }//GEN-LAST:event_undoMenuItemActionPerformed private void closeMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeMenuItemActionPerformed close(); }//GEN-LAST:event_closeMenuItemActionPerformed private void updateUndoStates() { undoMenuItem.setEnabled(undo.canUndo()); if (undo.canUndo()) undoMenuItem.setText(undo.getUndoPresentationName()); else undoMenuItem.setText("Can't undo"); redoMenuItem.setEnabled(undo.canRedo()); if (undo.canRedo()) redoMenuItem.setText(undo.getRedoPresentationName()); else redoMenuItem.setText("Can't redo"); } private static final Color COLOR_SYNTAX_OK = Color.GREEN.darker(); private static final Color COLOR_SYNTAX_NOTOK = Color.RED.darker(); private static final Color COLOR_HIGHLIGHT_ERROR = new Color(255, 128, 128, 128); // half-transparent light red /** * Checks the text area's text for validity. * @see #validateText() */ private void textAreaKeyTyped() { if (validateText()) { syntaxCheckLabel.setText("Syntax OK"); syntaxCheckLabel.setForeground(COLOR_SYNTAX_OK); } else { syntaxCheckLabel.setText("Syntax invalid"); syntaxCheckLabel.setForeground(COLOR_SYNTAX_NOTOK); } syntaxCheckLabel.setToolTipText(getUnmatchedBracketText()); highlightInvalidLines(); } private static final ParserSettings checkSettings = ParserSettings.getStrictSettings().setPrintTimingInfo(false); /** @return whether the data was parsed successfully or not. */ private boolean validateText() { return EUGFileIO.loadFromString(textPane.getText(), checkSettings) != null; } /** * Returns a mapping of line numbers to either 0 (left bracket) or 1 (right * bracket). */ private java.util.Map<Integer, Integer> findUnmatchedBrackets() { final Segment text = new Segment(); final java.util.Stack<Integer> lefts = new java.util.Stack<Integer>(); final java.util.Map<Integer, Integer> unmatched = new java.util.HashMap<Integer, Integer>(); final int lineCount = getLineCount(); for (int line = 0; line < lineCount; line++) { getLineText(line, text); for (int i = text.getBeginIndex(); i < text.getEndIndex(); i++) { char c = text.array[i]; if (c == '{') { lefts.push(line); } else if (c == '}') { if (lefts.isEmpty()) { unmatched.put(line, 1); } else { lefts.pop(); } } else if (c == '#') { break; // break line loop } } } while (!lefts.isEmpty()) { unmatched.put(lefts.pop(), 0); } return unmatched; } private void clearHighlights() { textPane.getHighlighter().removeAllHighlights(); } private void highlightInvalidLines() { clearHighlights(); for (Integer line : findUnmatchedBrackets().keySet()) { highlightErrorLine(line); } } private void highlightErrorLine(int line) { try { textPane.getHighlighter().addHighlight( getLineStartOffset(line), getLineEndOffset(line), new DefaultHighlighter.DefaultHighlightPainter(COLOR_HIGHLIGHT_ERROR) ); // TODO: Make error stripe } catch (BadLocationException ex) { ex.printStackTrace(); } } private String getUnmatchedBracketText() { final java.util.Map<Integer, Integer> brackets = new java.util.TreeMap<Integer, Integer>(findUnmatchedBrackets()); final StringBuilder text = new StringBuilder("<html>Unmatched brackets:<br>"); for (java.util.Map.Entry<Integer, Integer> entry : brackets.entrySet()) { if (entry.getValue() == 0) { text.append("Left bracket on line "); } else { text.append("Right bracket on line "); } text.append(entry.getKey()).append("<br>"); } text.append("</html>"); return text.toString(); } /** * Called when the dialog is closed by any of the three methods: the close * button, the close menu item, or the window decorations. * <p>Subclasses should override this method if they wish to, e.g., save * changes before closing. */ protected void close() { dispose(); EUGFileIO.save(config, "editor_cfg.txt", EUGFileIO.NO_COMMENT); } /** * Returns true iff the text in the text area has changed since being * loaded. */ public final boolean textHasChanged() { return !originalContents.equals(textPane.getText()); } protected final String getOriginalContents() { return originalContents; } /** Use with care! */ protected final void setOriginalContents(final String contents) { // All this replacing is a hack to get originalContents to match up with // the text pane's text. Apparently, any \n characters inserted into // the document automatically turn into \r\n (even if they were already // part of one!). originalContents = contents.replaceAll("(\r\n|\r|\n)", "\r\n"); try { Document newDoc = textPane.getEditorKit().createDefaultDocument(); newDoc.insertString(0, contents.replaceAll("\r\n", "\n"), null); textPane.setDocument(newDoc); } catch (BadLocationException ex) { ex.printStackTrace(); } initDocument(); textPane.setCaretPosition(0); textAreaKeyTyped(); undo.discardAllEdits(); updateUndoStates(); } public String getText() { return textPane.getText(); } // A few private utility methods private int getLineCount() { int lineCount = textPane.getDocument().getDefaultRootElement().getElementCount(); return lineCount; } private static final Pattern newLine = Pattern.compile("\\n"); private int getCaretLine() { try { final String text = textPane.getText(0, textPane.getCaretPosition()); return newLine.split(text, -1).length; } catch (BadLocationException ex) { ex.printStackTrace(); return -1; } } private int getLineStartOffset(int line) { // From JEditTextArea, with modifications Element lineElement = textPane.getDocument().getDefaultRootElement().getElement(line); if (lineElement == null) return -1; else return lineElement.getStartOffset(); } private int getLineEndOffset(int line) { // From JEditTextArea, with modifications Element lineElement = textPane.getDocument().getDefaultRootElement().getElement(line); if (lineElement == null) return -1; else return lineElement.getEndOffset(); } private final void getLineText(int lineIndex, Segment segment) { int start = getLineStartOffset(lineIndex); try { textPane.getDocument().getText(start, getLineEndOffset(lineIndex) - start - 1,segment); } catch (BadLocationException ex) { ex.printStackTrace(); } } private void cut() { textPane.cut(); } private void copy() { textPane.copy(); } private void paste() { textPane.paste(); } private void undo() { try { undo.undo(); } catch (CannotUndoException ex) { System.out.println("Unable to undo: " + ex); ex.printStackTrace(); } updateUndoStates(); textAreaKeyTyped(); } private void redo() { try { undo.redo(); } catch (CannotRedoException ex) { System.out.println("Unable to redo: " + ex); ex.printStackTrace(); } updateUndoStates(); textAreaKeyTyped(); } public static void main(String[] args) { new EditorDialog(null, "Test", "country = {\n\ttag = ENG\n\tai = \"england.ai\"\n\tmajor = yes\n\tbadboy = 0\n}") .setVisible(true); } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton closeButton; private javax.swing.JMenuItem closeMenuItem; private javax.swing.JMenuItem copyMenuItem; private javax.swing.JMenuItem cutMenuItem; private javax.swing.JMenuItem findMenuItem; private javax.swing.JMenuItem goToMenuItem; private javax.swing.JMenuItem pasteMenuItem; private javax.swing.JMenuItem redoMenuItem; private javax.swing.JMenuItem replaceMenuItem; private javax.swing.JMenuItem setFontMenuItem; private javax.swing.JMenuItem showCountryListMenuItem; private javax.swing.JMenuItem showCultureListMenuItem; private javax.swing.JMenuItem showProvinceListMenuItem; private javax.swing.JLabel syntaxCheckLabel; protected javax.swing.JEditorPane textPane; private javax.swing.JMenuItem undoMenuItem; // End of variables declaration//GEN-END:variables // ------------------------------------------------------------------- // Inner Classes // ------------------------------------------------------------------- //<editor-fold defaultstate="collapsed" desc=" FontSelectorDialog "> private static final class FontSelectorDialog extends JDialog { private JList nameList; // private JList typeList; private JList sizeList; private JLabel testLabel; boolean closedOK = false; private String name; private int type; private int size; private transient final ListSelectionListener listListener = new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { if (e.getSource() == nameList) name = (String)nameList.getSelectedValue(); // else if (e.getSource() == typeList) // type = getType((String)typeList.getSelectedValue()); else if (e.getSource() == sizeList) size = (Integer)sizeList.getSelectedValue(); else System.err.println(e); update(); } }; FontSelectorDialog(JDialog parent, String family, int type, int size) { super(parent, "Font selector", true); setLayout(new BorderLayout()); JPanel centerPanel = new JPanel(new BorderLayout(0, 5)); JPanel upperPanel = new JPanel(/*new GridLayout(0, 3)*/); JPanel namePanel = new JPanel(); namePanel.setBorder(BorderFactory.createTitledBorder("Name")); nameList = new JList(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()); nameList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); nameList.addListSelectionListener(listListener); namePanel.add(new JScrollPane(nameList)); upperPanel.add(namePanel); // JPanel typePanel = new JPanel(); // typePanel.setBorder(BorderFactory.createTitledBorder("Type")); // typeList = new JList(new String[] { "Plain", "Bold", "Italic", "Bold Italic" } ); // typeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // typeList.addListSelectionListener(listListener); // typePanel.add(new JScrollPane(typeList)); // upperPanel.add(typePanel); JPanel sizePanel = new JPanel(); sizePanel.setBorder(BorderFactory.createTitledBorder("Size")); sizeList = new JList(new Integer[] { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 28, 32, 36, 40 } ); sizeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); sizeList.addListSelectionListener(listListener); sizePanel.add(new JScrollPane(sizeList)); upperPanel.add(sizePanel); centerPanel.add(upperPanel, BorderLayout.CENTER); JPanel lowerPanel = new JPanel(); lowerPanel.setBorder(BorderFactory.createTitledBorder("Preview")); testLabel = new JLabel("Sample text"); lowerPanel.add(testLabel); centerPanel.add(lowerPanel, BorderLayout.SOUTH); add(centerPanel, BorderLayout.CENTER); JPanel bottomPanel = new JPanel(); final JButton okButton = new JButton("OK"); final JButton cancelButton = new JButton("Cancel"); ActionListener buttonListener = new ActionListener() { public void actionPerformed(ActionEvent e) { if (e.getSource() == okButton) closedOK = true; else closedOK = false; dispose(); } }; okButton.addActionListener(buttonListener); cancelButton.addActionListener(buttonListener); bottomPanel.add(okButton); bottomPanel.add(cancelButton); add(bottomPanel, BorderLayout.SOUTH); pack(); setLocationRelativeTo(parent); nameList.setSelectedValue(family, true); // typeList.setSelectedValue(getTypeName(type), true); sizeList.setSelectedValue(size, true); name = family; this.type = type; this.size = size; update(); } // private int getType(String type) { // if (type == null) // return Font.PLAIN; // // if (type.equalsIgnoreCase("plain")) // return Font.PLAIN; // else if (type.equalsIgnoreCase("bold")) // return Font.BOLD; // else if (type.equalsIgnoreCase("italic")) // return Font.ITALIC; // else if (type.equalsIgnoreCase("bold italic")) // return Font.BOLD | Font.ITALIC; // else // return Font.PLAIN; // } // // private String getTypeName(int type) { // if ((type & Font.BOLD) != 0) { // // bold, now check italic // if ((type & Font.ITALIC) != 0) // return "Bold Italic"; // else // return "Bold"; // } else if ((type & Font.ITALIC) != 0) { // return "Italic"; // } else { // return "Plain"; // } // } private void update() { testLabel.setFont(getSelectedFont()); } public Font getSelectedFont() { return new Font(name, type, size); } } //</editor-fold> //<editor-fold defaultstate="collapsed" desc=" FindDialog "> private final class FindDialog extends JDialog { private JTextField findField; private JTextField replaceField; private JRadioButton forwardButton; private JRadioButton backwardButton; private JCheckBox matchCaseCheckBox; FindDialog() { super(EditorDialog.this, "Find"); setLayout(new BorderLayout()); JPanel centerPanel = new JPanel(new GridLayout(0, 1)); JPanel findPanel = new JPanel(); findPanel.add(new JLabel("Find: ")); findField = new JTextField(textPane.getSelectedText()); findField.setPreferredSize(new java.awt.Dimension(100, 25)); findField.addActionListener(new FindListener()); findPanel.add(findField); centerPanel.add(findPanel); JPanel replacePanel = new JPanel(); replacePanel.add(new JLabel("Replace: ")); replaceField = new JTextField(); replaceField.setPreferredSize(new java.awt.Dimension(100, 25)); replaceField.setEnabled(false); replaceField.setToolTipText("Replace is not yet supported."); replacePanel.add(replaceField); centerPanel.add(replacePanel); add(centerPanel, BorderLayout.CENTER); JPanel lowerPanel = new JPanel(); JPanel directionPanel = new JPanel(); directionPanel.setBorder(BorderFactory.createTitledBorder("Direction")); ButtonGroup directionButtonGroup = new ButtonGroup(); forwardButton = new JRadioButton("Forward", true); directionButtonGroup.add(forwardButton); directionPanel.add(forwardButton); backwardButton = new JRadioButton("Backward", false); backwardButton.setEnabled(false); backwardButton.setToolTipText("Backward searching is not yet supported."); directionButtonGroup.add(backwardButton); directionPanel.add(backwardButton); lowerPanel.add(directionPanel); matchCaseCheckBox = new JCheckBox("Match case"); matchCaseCheckBox.setSelected(true); matchCaseCheckBox.setEnabled(false); matchCaseCheckBox.setToolTipText("Case insensitive searching is not yet supported."); lowerPanel.add(matchCaseCheckBox); add(lowerPanel, BorderLayout.SOUTH); JPanel rightPanel = new JPanel(); JButton findButton = new JButton(new FindListener()); rightPanel.add(findButton); JButton replaceButton = new JButton("Replace"); replaceButton.setEnabled(false); replaceButton.setToolTipText("Replace is not yet supported."); rightPanel.add(replaceButton); add(rightPanel, BorderLayout.EAST); pack(); setLocationRelativeTo(EditorDialog.this); } private class FindListener extends AbstractAction { FindListener() { super("Find"); putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK)); putValue(MNEMONIC_KEY, KeyEvent.VK_F); } public void actionPerformed(ActionEvent e) { final Segment text = new Segment(); final String findText = findField.getText(); final int lineCount = getLineCount(); // final boolean matchCase = matchCaseCheckBox.isSelected(); // final String pattern = Pattern.quote(findText); int idx; if (forwardButton.isSelected()) { lineLoop: for (int line = 0; line < lineCount; line++) { int lineStart = getLineStartOffset(line); if (line < lineCount-1 && getLineStartOffset(line+1) < textPane.getCaretPosition()) continue; getLineText(line, text); if (text.toString().contains(findText)) { // try to figure out where it is // System.out.println("Found on line " + line); for (int i = 0; i < text.count; i++) { if (lineStart + i < textPane.getCaretPosition()) continue; // System.out.println(text.toString()); String remainder = new String(text.array, text.offset+i, text.count-i); // System.out.println("remainder = " + remainder); idx = remainder.indexOf(findText); if (idx < 0) continue; else { idx += lineStart + i; textPane.setCaretPosition(idx); textPane.select(idx, idx + findText.length()); // break lineLoop; return; } } // System.err.println("Something is wrong in FindDialog"); } } } else { // Backwards // JOptionPane.showMessageDialog(EditorDialog.FindDialog.this, // "Backwards search not yet supported."); lineLoop: for (int line = lineCount-1; line >= 0; line--) { // loop until the line start is before the caret int lineStart = getLineStartOffset(line); if (lineStart >= textPane.getCaretPosition()) continue; getLineText(line, text); if (text.toString().contains(findText)) { // try to figure out where it is System.out.println("Found on line " + line); for (int i = text.count-1; i >= 0; i--) { // loop until i is at or before the caret if (lineStart + i > textPane.getCaretPosition()) continue; System.out.println(text.toString()); String remainder = new String(text.array, text.offset, text.count-i); //text.subSequence(0, i).toString(); System.out.println("remainder = " + remainder); idx = remainder.indexOf(findText); if (idx < 0) continue; else { idx += lineStart + i; textPane.setCaretPosition(idx); textPane.select(idx, idx + findText.length()); // break lineLoop; return; } } // System.err.println("Something is wrong in FindDialog"); } } } JOptionPane.showMessageDialog(FindDialog.this, "String '" + findText + "' not found."); } } } //</editor-fold> private static final double javaVersion = Double.parseDouble(System.getProperty("java.version").substring(0, 3)); private static final boolean supportsRowSorter = (javaVersion >= 1.6); //<editor-fold defaultstate="collapsed" desc=" CountryListDialog "> private static final class CountryListDialog extends JDialog { CountryListDialog(java.awt.Frame parent) { super(parent, "Countries", false); GenericObject countries = EUGFileIO.load(Main.filenameResolver.resolveFilename("common/countries.txt"), ParserSettings.getNoCommentSettings()); String[][] tagNameTable = new String[countries.size()][2]; int i = 0; for (eug.shared.ObjectVariable var : countries.values) { tagNameTable[i][0] = var.varname; tagNameTable[i][1] = Text.getText(var.varname); i++; } JTable countryTable = new JTable(tagNameTable, new String[] { "Tag", "Name" } ); countryTable.setDefaultEditor(Object.class, null); if (supportsRowSorter) { countryTable.setAutoCreateRowSorter(true); } else { System.out.println("Table sorting not supported."); } setLayout(new BorderLayout()); add(new JScrollPane(countryTable), BorderLayout.CENTER); setDefaultCloseOperation(DISPOSE_ON_CLOSE); pack(); } private static CountryListDialog dialog; public static void showDialog(java.awt.Frame parent) { if (dialog == null) { dialog = new CountryListDialog(parent); } dialog.setVisible(true); } } //</editor-fold> //<editor-fold defaultstate="collapsed" desc=" ProvinceListDialog "> private static final class ProvinceListDialog extends JDialog { ProvinceListDialog(java.awt.Frame parent) { super(parent, "Provinces", false); ProvinceData.Province[] provs = Main.provinceData.getAllProvs(); String[][] provNameTable = new String[provs.length][3]; int i = 0; boolean pti = true; for (ProvinceData.Province prov : provs) { provNameTable[i][0] = Integer.toString(prov.getId()); provNameTable[i][1] = prov.getName(); if (pti) { provNameTable[i][2] = Text.getText("terra_incognita"); pti = false; } else { provNameTable[i][2] = Text.getText("prov" + prov.getId()); } i++; } JTable provTable = new JTable(provNameTable, new String[] { "Tag", "Name in definition", "Display name" } ); provTable.setDefaultEditor(Object.class, null); if (supportsRowSorter) { provTable.setAutoCreateRowSorter(true); } else { System.out.println("Table sorting not supported."); } setLayout(new BorderLayout()); add(new JScrollPane(provTable), BorderLayout.CENTER); setDefaultCloseOperation(DISPOSE_ON_CLOSE); pack(); } private static ProvinceListDialog dialog; public static void showDialog(java.awt.Frame parent) { if (dialog == null) { dialog = new ProvinceListDialog(parent); } dialog.setVisible(true); } } //</editor-fold> //<editor-fold defaultstate="collapsed" desc=" CultureListDialog "> private static final class CultureListDialog extends JDialog { CultureListDialog(java.awt.Frame parent) { super(parent, "Cultures", false); GenericObject cultureGroups = EUGFileIO.load(Main.filenameResolver.resolveFilename("common/cultures.txt"), ParserSettings.getNoCommentSettings()); // No easy way to do this with an array, so use vector instead. Vector<Vector<String>> cultureGroupNameTable = new Vector<Vector<String>>(); for (GenericList group : cultureGroups.lists) { String groupName = group.getName(); // String groupText = Text.getText(groupName); for (String culture : group) { Vector<String> vector = new Vector<String>(2); vector.add(groupName); // vector.add(groupText); vector.add(culture); // vector.add(Text.getText(culture)); cultureGroupNameTable.add(vector); } } Vector<String> labels = new Vector<String>(4); labels.add("Group"); // labels.add("Group display name"); labels.add("Culture"); // labels.add("Culture display name"); JTable cultureTable = new JTable(cultureGroupNameTable, labels); cultureTable.setDefaultEditor(Object.class, null); if (supportsRowSorter) { cultureTable.setAutoCreateRowSorter(true); } else { System.out.println("Table sorting not supported."); } setLayout(new BorderLayout()); add(new JScrollPane(cultureTable), BorderLayout.CENTER); setDefaultCloseOperation(DISPOSE_ON_CLOSE); pack(); } private static CultureListDialog dialog; public static void showDialog(java.awt.Frame parent) { if (dialog == null) { dialog = new CultureListDialog(parent); } dialog.setVisible(true); } } //</editor-fold> //<editor-fold defaultstate="collapsed" desc=" ActionListeners "> //{{{ ActionListeners // private class CopyListener extends AbstractAction implements ActionListener { // public void actionPerformed(ActionEvent e) { // copy(); // } // } // // private class CutListener extends AbstractAction implements ActionListener { // public void actionPerformed(ActionEvent e) { // cut(); // } // } // // private class PasteListener extends AbstractAction implements ActionListener { // public void actionPerformed(ActionEvent e) { // paste(); // } // } // // private class UndoListener extends AbstractAction implements ActionListener { // public void actionPerformed(ActionEvent e) { // undo(); // } // } // // private class RedoListener extends AbstractAction implements ActionListener { // public void actionPerformed(ActionEvent e) { // redo(); // } // } // // private class GoToListener extends AbstractAction implements ActionListener { // public void actionPerformed(ActionEvent e) { // int lineCount = getLineCount(); // int caretLine = getCaretLine(); // String response = // JOptionPane.showInputDialog(EditorDialog.this, // "Go to line (0 - " + (lineCount-1) + "): ", caretLine); // // if (response == null || response.length() == 0) // return; // // textPane.setCaretPosition(getLineStartOffset(Integer.parseInt(response))); // } // } //}}} //</editor-fold> }