// Near Infinity - An Infinity Engine Browser and Editor // Copyright (C) 2001 - 2005 Jon Olav Hauglid // See LICENSE.txt for license information package org.infinity.resource.dlg; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.List; import java.util.SortedMap; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import org.infinity.datatype.Datatype; import org.infinity.datatype.DecNumber; import org.infinity.datatype.Editable; import org.infinity.datatype.IsTextual; import org.infinity.datatype.TextString; import org.infinity.datatype.UpdateEvent; import org.infinity.gui.BrowserMenuBar; import org.infinity.gui.ButtonPanel; import org.infinity.gui.ButtonPopupMenu; import org.infinity.gui.InfinityScrollPane; import org.infinity.gui.ScriptTextArea; import org.infinity.gui.StructViewer; import org.infinity.icon.Icons; import org.infinity.resource.AbstractStruct; import org.infinity.resource.AddRemovable; import org.infinity.resource.StructEntry; import org.infinity.resource.bcs.Compiler; import org.infinity.util.io.StreamUtils; public abstract class AbstractCode extends Datatype implements Editable, IsTextual, AddRemovable, ActionListener, DocumentListener, ItemListener { // DLG/AbstractCode-specific field labels public static final String DLG_CODE_OFFSET = "Offset"; public static final String DLG_CODE_LENGTH = "Length"; public static final String DLG_CODE_TEXT = "Text"; private static final ButtonPanel.Control CtrlUpdate = ButtonPanel.Control.CUSTOM_1; private static final ButtonPanel.Control CtrlCheck = ButtonPanel.Control.CUSTOM_2; private static final ButtonPanel.Control CtrlErrors = ButtonPanel.Control.CUSTOM_3; private static final ButtonPanel.Control CtrlWarnings = ButtonPanel.Control.CUSTOM_4; private final ButtonPanel buttonPanel = new ButtonPanel(); private DecNumber len; private DecNumber off; private ScriptTextArea textArea; private SortedMap<Integer, String> errors, warnings; private String text; AbstractCode(String name) { this(StreamUtils.getByteBuffer(8), 0, name); text = ""; } AbstractCode(ByteBuffer buffer, int offset, String nane) { super(offset, 8, nane); read(buffer, offset); text = (len.getValue() > 0) ? StreamUtils.readString(buffer, off.getValue(), len.getValue()) : ""; } // --------------------- Begin Interface ActionListener --------------------- @Override public void actionPerformed(ActionEvent event) { if (buttonPanel.getControlByType(CtrlCheck) == event.getSource()) { JButton bCheck = (JButton)event.getSource(); ButtonPopupMenu bpmErrors = (ButtonPopupMenu)buttonPanel.getControlByType(CtrlErrors); ButtonPopupMenu bpmWarnings = (ButtonPopupMenu)buttonPanel.getControlByType(CtrlWarnings); Compiler compiler = new Compiler(textArea.getText(), (this instanceof Action) ? Compiler.ScriptType.ACTION : Compiler.ScriptType.TRIGGER); errors = compiler.getErrors(); warnings = compiler.getWarnings(); if (errors.size() > 0) { JMenuItem errorItems[] = new JMenuItem[errors.size()]; int count = 0; for (Integer lineNr : errors.keySet()) { String error = errors.get(lineNr); errorItems[count++] = new JMenuItem(lineNr.toString() + ": " + error); } bpmErrors.setMenuItems(errorItems); } if (warnings.size() > 0) { JMenuItem warningItems[] = new JMenuItem[warnings.size()]; int count = 0; for (Integer lineNr : warnings.keySet()) { String warning = warnings.get(lineNr); warningItems[count++] = new JMenuItem(lineNr.toString() + ": " + warning); } bpmWarnings.setMenuItems(warningItems); } bpmErrors.setEnabled(errors.size() > 0); bpmWarnings.setEnabled(warnings.size() > 0); bpmErrors.setText("Errors (" + errors.size() + ")..."); bpmWarnings.setText("Warnings (" + warnings.size() + ")..."); bCheck.setEnabled(false); } } // --------------------- End Interface ActionListener --------------------- // --------------------- Begin Interface DocumentListener --------------------- @Override public void insertUpdate(DocumentEvent event) { buttonPanel.getControlByType(CtrlUpdate).setEnabled(true); buttonPanel.getControlByType(CtrlCheck).setEnabled(true); } @Override public void removeUpdate(DocumentEvent event) { buttonPanel.getControlByType(CtrlUpdate).setEnabled(true); buttonPanel.getControlByType(CtrlCheck).setEnabled(true); } @Override public void changedUpdate(DocumentEvent event) { buttonPanel.getControlByType(CtrlUpdate).setEnabled(true); buttonPanel.getControlByType(CtrlCheck).setEnabled(true); } // --------------------- End Interface DocumentListener --------------------- // --------------------- Begin Interface Editable --------------------- @Override public JComponent edit(ActionListener container) { textArea = new ScriptTextArea(); textArea.setMargin(new Insets(3, 3, 3, 3)); String convertedText = text; int index = convertedText.indexOf((int)'\r'); while (index != -1) { convertedText = convertedText.substring(0, index) + convertedText.substring(index + 1); index = convertedText.indexOf((int)'\r'); } textArea.setText(convertedText); textArea.setCaretPosition(0); textArea.getDocument().addDocumentListener(this); JButton bUpdate = new JButton("Update", Icons.getIcon(Icons.ICON_REFRESH_16)); bUpdate.addActionListener(container); bUpdate.setActionCommand(StructViewer.UPDATE_VALUE); bUpdate.setEnabled(false); buttonPanel.addControl(bUpdate, CtrlUpdate); JButton bCheck = new JButton("Compile Check", Icons.getIcon(Icons.ICON_REDO_16)); bCheck.addActionListener(this); buttonPanel.addControl(bCheck, CtrlCheck); ButtonPopupMenu bpmErrors = new ButtonPopupMenu("Errors (0)...", new JMenuItem[]{}); bpmErrors.setIcon(Icons.getIcon(Icons.ICON_UP_16)); bpmErrors.addItemListener(this); bpmErrors.setEnabled(false); buttonPanel.addControl(bpmErrors, CtrlErrors); ButtonPopupMenu bpmWarnings = new ButtonPopupMenu("Warnings (0)...", new JMenuItem[]{}); bpmWarnings.setIcon(Icons.getIcon(Icons.ICON_UP_16)); bpmWarnings.addItemListener(this); bpmWarnings.setEnabled(false); buttonPanel.addControl(bpmWarnings, CtrlWarnings); InfinityScrollPane scroll = new InfinityScrollPane(textArea, true); GridBagLayout gbl = new GridBagLayout(); GridBagConstraints gbc = new GridBagConstraints(); JPanel panel = new JPanel(gbl); gbc.weightx = 1.0; gbc.weighty = 1.0; gbc.fill = GridBagConstraints.BOTH; gbc.insets.left = 5; gbc.insets.right = 5; gbc.gridwidth = GridBagConstraints.REMAINDER; gbl.setConstraints(scroll, gbc); panel.add(scroll); gbc.weighty = 0.0; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets.top = 3; gbc.insets.left = 0; gbl.setConstraints(buttonPanel, gbc); panel.add(buttonPanel); panel.setMinimumSize(DIM_BROAD); panel.setPreferredSize(DIM_BROAD); return panel; } @Override public void select() { if (BrowserMenuBar.getInstance().autocheckBCS()) { ((JButton)buttonPanel.getControlByType(CtrlCheck)).doClick(); } } @Override public boolean updateValue(AbstractStruct struct) { JButton bCheck = (JButton)buttonPanel.getControlByType(CtrlCheck); JButton bUpdate = (JButton)buttonPanel.getControlByType(CtrlUpdate); if (bCheck.isEnabled()) bCheck.doClick(); if (errors.size() > 0) { String options[] = {"Update", "Cancel"}; if (JOptionPane.showOptionDialog(textArea.getTopLevelAncestor(), "Errors exist. Update anyway?", "Update value", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]) != 0) { // notifying listeners fireValueUpdated(new UpdateEvent(this, struct)); return true; } } text = textArea.getText(); int index = text.indexOf((int)'\n'); while (index != -1) { text = text.substring(0, index) + '\r' + text.substring(index); index = text.indexOf((int)'\n', index + 2); } bUpdate.setEnabled(false); // notifying listeners fireValueUpdated(new UpdateEvent(this, struct)); return true; } // --------------------- End Interface Editable --------------------- //--------------------- Begin Interface AddRemovable --------------------- @Override public boolean canRemove() { return true; } //--------------------- End Interface AddRemovable --------------------- // --------------------- Begin Interface ItemListener --------------------- @Override public void itemStateChanged(ItemEvent event) { ButtonPopupMenu bpmErrors = (ButtonPopupMenu)buttonPanel.getControlByType(CtrlErrors); ButtonPopupMenu bpmWarnings = (ButtonPopupMenu)buttonPanel.getControlByType(CtrlWarnings); String selected = ""; if (event.getSource() == bpmErrors) selected = bpmErrors.getSelectedItem().getText(); else if (event.getSource() == bpmWarnings) selected = bpmWarnings.getSelectedItem().getText(); int index = selected.indexOf(": "); int line = Integer.parseInt(selected.substring(0, index)); highlightLine(line); } // --------------------- End Interface ItemListener --------------------- // --------------------- Begin Interface Writeable --------------------- @Override public void write(OutputStream os) throws IOException { off.write(os); len.write(os); } // --------------------- End Interface Writeable --------------------- // --------------------- Begin Interface Readable --------------------- @Override public int read(ByteBuffer buffer, int offset) { off = new DecNumber(buffer, offset, 4, DLG_CODE_OFFSET); len = new DecNumber(buffer, offset + 4, 4, DLG_CODE_LENGTH); return offset + getSize(); } // --------------------- End Interface Readable --------------------- // --------------------- Begin Interface IsTextual --------------------- @Override public String getText() { return text; } // --------------------- End Interface IsTextual --------------------- @Override public String toString() { return getText(); } public void addFlatList(List<StructEntry> flatList) { flatList.add(off); flatList.add(len); try { TextString ts = new TextString(StreamUtils.getByteBuffer(text.getBytes()), 0, len.getValue(), DLG_CODE_TEXT); ts.setOffset(off.getValue()); flatList.add(ts); } catch (Exception e) { e.printStackTrace(); } } public int getTextLength() { return len.getValue(); } public int getTextOffset() { return off.getValue(); } public int updateOffset(int offs) { off.setValue(offs); len.setValue(text.length()); return len.getValue(); } public void writeString(OutputStream os) throws IOException { StreamUtils.writeString(os, text, len.getValue()); } private void highlightLine(int linenr) { String s = textArea.getText() + '\n'; int startpos = 0; for (int i = 1; i < linenr; i++) startpos = s.indexOf("\n", startpos + 1); if (startpos == -1) return; int endpos = s.indexOf("\n", startpos + 1); textArea.select(startpos, endpos); textArea.getCaret().setSelectionVisible(true); } }