// 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.text;
import java.awt.BorderLayout;
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.io.OutputStreamWriter;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.infinity.gui.BrowserMenuBar;
import org.infinity.gui.ButtonPanel;
import org.infinity.gui.ButtonPopupMenu;
import org.infinity.gui.InfinityScrollPane;
import org.infinity.gui.InfinityTextArea;
import org.infinity.resource.Closeable;
import org.infinity.resource.Profile;
import org.infinity.resource.ResourceFactory;
import org.infinity.resource.TextResource;
import org.infinity.resource.ViewableContainer;
import org.infinity.resource.Writeable;
import org.infinity.resource.key.BIFFResourceEntry;
import org.infinity.resource.key.ResourceEntry;
import org.infinity.search.TextResourceSearcher;
import org.infinity.util.Decryptor;
import org.infinity.util.Misc;
import org.infinity.util.io.FileManager;
import org.infinity.util.io.StreamUtils;
public final class PlainTextResource implements TextResource, Writeable, ActionListener, ItemListener,
DocumentListener, Closeable
{
private final ResourceEntry entry;
private final String text;
private final ButtonPanel buttonPanel = new ButtonPanel();
private JMenuItem ifindall, ifindthis;
private JPanel panel;
private InfinityTextArea editor;
private boolean resourceChanged;
private int highlightedLine;
public PlainTextResource(ResourceEntry entry) throws Exception
{
this(entry, -1);
}
public PlainTextResource(ResourceEntry entry, int highlightedLine) throws Exception
{
this.entry = entry;
ByteBuffer buffer = entry.getResourceBuffer();
if (buffer.limit() > 1 && buffer.getShort(0) == -1) {
buffer = Decryptor.decrypt(buffer, 2);
}
Charset cs = null;
if (BrowserMenuBar.getInstance() != null) {
cs = Charset.forName(BrowserMenuBar.getInstance().getSelectedCharset());
} else {
cs = Misc.CHARSET_DEFAULT;
}
text = StreamUtils.readString(buffer, buffer.limit(), cs);
this.highlightedLine = highlightedLine;
}
// --------------------- Begin Interface ActionListener ---------------------
@Override
public void actionPerformed(ActionEvent event)
{
if (buttonPanel.getControlByType(ButtonPanel.Control.SAVE) == event.getSource()) {
if (ResourceFactory.saveResource(this, panel.getTopLevelAncestor()))
resourceChanged = false;
} else if (buttonPanel.getControlByType(ButtonPanel.Control.EXPORT_BUTTON) == event.getSource()) {
ResourceFactory.exportResource(entry, panel.getTopLevelAncestor());
} else if (buttonPanel.getControlByType(ButtonPanel.Control.TRIM_SPACES) == event.getSource()) {
StringBuffer newText = new StringBuffer(editor.getText().length());
StringTokenizer st = new StringTokenizer(editor.getText(), "\n");
while (st.hasMoreTokens()) {
newText.append(st.nextToken().trim()).append('\n');
}
editor.setText(newText.toString());
editor.setCaretPosition(0);
}
}
// --------------------- End Interface ActionListener ---------------------
// --------------------- Begin Interface Closeable ---------------------
@Override
public void close() throws Exception
{
if (resourceChanged) {
Path output;
if (entry instanceof BIFFResourceEntry) {
output = FileManager.query(Profile.getGameRoot(), Profile.getOverrideFolderName(), entry.toString());
} else {
output = entry.getActualPath();
}
String options[] = {"Save changes", "Discard changes", "Cancel"};
int result = JOptionPane.showOptionDialog(panel, "Save changes to " + output + '?', "Resource changed",
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.WARNING_MESSAGE, null, options, options[0]);
if (result == 0) {
ResourceFactory.saveResource(this, panel.getTopLevelAncestor());
} else if (result != 1) {
throw new Exception("Save aborted");
}
}
}
// --------------------- End Interface Closeable ---------------------
// --------------------- Begin Interface DocumentListener ---------------------
@Override
public void insertUpdate(DocumentEvent event)
{
resourceChanged = true;
}
@Override
public void removeUpdate(DocumentEvent event)
{
resourceChanged = true;
}
@Override
public void changedUpdate(DocumentEvent event)
{
resourceChanged = true;
}
// --------------------- End Interface DocumentListener ---------------------
// --------------------- Begin Interface ItemListener ---------------------
@Override
public void itemStateChanged(ItemEvent event)
{
ButtonPopupMenu bpmFind = (ButtonPopupMenu)buttonPanel.getControlByType(ButtonPanel.Control.FIND_MENU);
if (event.getSource() == bpmFind) {
if (bpmFind.getSelectedItem() == ifindall) {
String type = entry.toString().substring(entry.toString().indexOf(".") + 1);
List<ResourceEntry> files = ResourceFactory.getResources(type);
new TextResourceSearcher(files, panel.getTopLevelAncestor());
} else if (bpmFind.getSelectedItem() == ifindthis) {
List<ResourceEntry> files = new ArrayList<ResourceEntry>();
files.add(entry);
new TextResourceSearcher(files, panel.getTopLevelAncestor());
}
}
}
// --------------------- End Interface ItemListener ---------------------
// --------------------- Begin Interface Resource ---------------------
@Override
public ResourceEntry getResourceEntry()
{
return entry;
}
// --------------------- End Interface Resource ---------------------
// --------------------- Begin Interface TextResource ---------------------
@Override
public String getText()
{
return text;
}
@Override
public void highlightText(int linenr, String text)
{
String s = editor.getText();
int startpos = 0;
int i = (s.charAt(0) == '\n') ? 2 : 1;
for (; i < linenr; i++) {
startpos = s.indexOf("\n", startpos + 1);
}
if (startpos == -1) return;
if (text != null) {
// try to select specified text string
int wordpos = s.toUpperCase(Locale.ENGLISH).indexOf(text.toUpperCase(Locale.ENGLISH), startpos);
if (wordpos != -1) {
editor.select(wordpos, wordpos + text.length());
} else {
editor.select(startpos, s.indexOf("\n", startpos + 1));
}
} else {
// select whole line
int endpos = s.indexOf("\n", startpos + 1);
if (endpos < 0) {
endpos = s.length();
}
editor.select(startpos, endpos);
}
editor.getCaret().setSelectionVisible(true);
}
// --------------------- End Interface TextResource ---------------------
// --------------------- Begin Interface Viewable ---------------------
@Override
public JComponent makeViewer(ViewableContainer container)
{
editor = new InfinityTextArea(text, true);
InfinityScrollPane pane = new InfinityScrollPane(editor, true);
setSyntaxHighlightingEnabled(editor, pane);
editor.addCaretListener(container.getStatusBar());
editor.setFont(BrowserMenuBar.getInstance().getScriptFont());
editor.setMargin(new Insets(3, 3, 3, 3));
editor.setCaretPosition(0);
editor.setLineWrap(false);
editor.getDocument().addDocumentListener(this);
if (entry.toString().toUpperCase(Locale.ENGLISH).endsWith(".BIO") ||
entry.toString().toUpperCase(Locale.ENGLISH).endsWith(".RES")) {
editor.setLineWrap(true);
editor.setWrapStyleWord(true);
}
ifindall =
new JMenuItem("in all " + entry.toString().substring(entry.toString().indexOf(".") + 1) + " files");
ifindthis = new JMenuItem("in this file only");
ButtonPopupMenu bpmFind = (ButtonPopupMenu)buttonPanel.addControl(ButtonPanel.Control.FIND_MENU);
bpmFind.setMenuItems(new JMenuItem[]{ifindall, ifindthis});
bpmFind.addItemListener(this);
((JButton)buttonPanel.addControl(ButtonPanel.Control.TRIM_SPACES)).addActionListener(this);
((JButton)buttonPanel.addControl(ButtonPanel.Control.EXPORT_BUTTON)).addActionListener(this);
((JButton)buttonPanel.addControl(ButtonPanel.Control.SAVE)).addActionListener(this);
panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(pane, BorderLayout.CENTER);
panel.add(buttonPanel, BorderLayout.SOUTH);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run()
{
if (highlightedLine >= 0) {
highlightText(highlightedLine, null);
}
}
});
return panel;
}
// --------------------- End Interface Viewable ---------------------
// --------------------- Begin Interface Writeable ---------------------
@Override
public void write(OutputStream os) throws IOException
{
if (editor == null) {
StreamUtils.writeString(os, text, text.length());
} else {
editor.write(new OutputStreamWriter(os));
}
}
// --------------------- End Interface Writeable ---------------------
public void setHighlightedLine(int highlightedLine)
{
this.highlightedLine = highlightedLine;
if (panel != null) {
highlightText(highlightedLine, null);
}
}
public int getHighlightedLine()
{
return highlightedLine;
}
public List<String> extract2DAHeaders()
{
StringTokenizer st = new StringTokenizer(getText(), "\n");
st.nextToken();
st.nextToken();
String header = st.nextToken();
st = new StringTokenizer(header);
List<String> strings = new ArrayList<String>();
while (st.hasMoreTokens())
strings.add(st.nextToken().toUpperCase(Locale.ENGLISH));
return strings;
}
private void setSyntaxHighlightingEnabled(InfinityTextArea edit, InfinityScrollPane pane)
{
InfinityTextArea.Language language = InfinityTextArea.Language.NONE;
if (entry != null) {
if ("SQL".equalsIgnoreCase(entry.getExtension())) {
if (BrowserMenuBar.getInstance() == null ||
BrowserMenuBar.getInstance().getSqlSyntaxHighlightingEnabled()) {
language = InfinityTextArea.Language.SQL;
}
} else if ("LUA".equalsIgnoreCase(entry.getExtension())) {
if (BrowserMenuBar.getInstance() == null ||
BrowserMenuBar.getInstance().getLuaSyntaxHighlightingEnabled()) {
language = InfinityTextArea.Language.LUA;
}
} else if (Profile.isEnhancedEdition() && "BALDUR.INI".equalsIgnoreCase(entry.getResourceName())) {
if (BrowserMenuBar.getInstance() == null ||
BrowserMenuBar.getInstance().getSqlSyntaxHighlightingEnabled()) {
language = InfinityTextArea.Language.SQL;
}
} else if ("GLSL".equalsIgnoreCase(entry.getExtension())) {
if (BrowserMenuBar.getInstance() == null ||
BrowserMenuBar.getInstance().getGlslSyntaxHighlightingEnabled()) {
language = InfinityTextArea.Language.GLSL;
}
} else if ("BCS".equalsIgnoreCase(entry.getExtension()) ||
"BS".equalsIgnoreCase(entry.getExtension()) ||
"BAF".equalsIgnoreCase(entry.getExtension())) {
if (BrowserMenuBar.getInstance() == null ||
BrowserMenuBar.getInstance().getBcsSyntaxHighlightingEnabled()) {
language = InfinityTextArea.Language.BCS;
}
}
}
if (edit != null) {
edit.applyExtendedSettings(language, null);
}
if (pane != null) {
pane.applyExtendedSettings(language);
}
}
}