// Near Infinity - An Infinity Engine Browser and Editor // Copyright (C) 2001 - 2005 Jon Olav Hauglid // See LICENSE.txt for license information package org.infinity.datatype; 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.MouseAdapter; import java.awt.event.MouseEvent; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Locale; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.infinity.gui.BrowserMenuBar; import org.infinity.gui.StructViewer; import org.infinity.gui.TextListPanel; import org.infinity.gui.ViewFrame; import org.infinity.icon.Icons; import org.infinity.resource.AbstractStruct; import org.infinity.resource.ResourceFactory; import org.infinity.resource.StructEntry; import org.infinity.resource.key.ResourceEntry; import org.infinity.util.io.StreamUtils; public class ResourceRef extends Datatype implements Editable, IsTextual, IsReference, ActionListener, ListSelectionListener { private static final Comparator<Object> ignoreCaseExtComparator = new IgnoreCaseExtComparator(); private static final String NONE = "None"; private final String[] type; private final ByteBuffer buffer; private String curtype; private String resname; private JButton bView; private TextListPanel list; public ResourceRef(ByteBuffer h_buffer, int offset, String name, String type) { this(null, h_buffer, offset, 8, name, type); } public ResourceRef(StructEntry parent, ByteBuffer h_buffer, int offset, String name, String type) { this(parent, h_buffer, offset, 8, name, type); } public ResourceRef(ByteBuffer h_buffer, int offset, int length, String name, String type) { this(null, h_buffer, offset, length, name, new String[]{type}); } public ResourceRef(StructEntry parent, ByteBuffer h_buffer, int offset, int length, String name, String type) { this(parent, h_buffer, offset, length, name, new String[]{type}); } public ResourceRef(ByteBuffer h_buffer, int offset, String name, String[] type) { this(null, h_buffer, offset, 8, name, type); } public ResourceRef(StructEntry parent, ByteBuffer h_buffer, int offset, String name, String[] type) { this(parent, h_buffer, offset, 8, name, type); } public ResourceRef(ByteBuffer h_buffer, int offset, int length, String name, String[] type) { this(null, h_buffer, offset, length, name, type); } public ResourceRef(StructEntry parent, ByteBuffer h_buffer, int offset, int length, String name, String[] type) { super(parent, offset, length, name); this.buffer = StreamUtils.getByteBuffer(length); if (type == null || type.length == 0) { this.type = new String[]{""}; } else { this.type = type; } this.curtype = this.type[0]; read(h_buffer, offset); } // --------------------- Begin Interface ActionListener --------------------- @Override public void actionPerformed(ActionEvent event) { if (event.getSource() == bView) { Object selected = list.getSelectedValue(); if (selected == NONE) { new ViewFrame(list.getTopLevelAncestor(), null); } else { ResourceEntry entry = ((ResourceRefEntry)selected).entry; if (entry != null) { new ViewFrame(list.getTopLevelAncestor(), ResourceFactory.getResource(entry)); } } } } // --------------------- End Interface ActionListener --------------------- // --------------------- Begin Interface Editable --------------------- @Override public JComponent edit(final ActionListener container) { List<List<ResourceEntry>> resourceList = new ArrayList<List<ResourceEntry>>(type.length); int entrynum = 0; for (int i = 0; i < type.length; i++) { resourceList.add(ResourceFactory.getResources(type[i])); entrynum += resourceList.get(i).size(); } List<Object> values = new ArrayList<Object>(1 + entrynum); values.add(NONE); for (int i = 0; i < type.length; i++) { for (int j = 0; j < resourceList.get(i).size(); j++) { ResourceEntry entry = resourceList.get(i).get(j); if (entry.toString().lastIndexOf('.') <= 8 && isLegalEntry(entry)) values.add(new ResourceRefEntry(entry)); } addExtraEntries(values); } Collections.sort(values, ignoreCaseExtComparator); list = new TextListPanel(values, false); list.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent event) { if (event.getClickCount() == 2) container.actionPerformed(new ActionEvent(this, 0, StructViewer.UPDATE_VALUE)); } }); ResourceEntry entry = null; for (int i = 0; i < type.length && entry == null; i++) { entry = ResourceFactory.getResourceEntry(resname + '.' + type[i], true); if (entry != null) { for (int j = 0; j < values.size(); j++) { Object o = values.get(j); if (o instanceof ResourceRefEntry && ((ResourceRefEntry)o).entry.equals(entry)) { list.setSelectedValue(o, true); break; } } } } if (entry == null) { list.setSelectedValue(NONE, true); for (int j = 0; j < values.size(); j++) { Object o = values.get(j); if (o instanceof ResourceRefEntry && ((ResourceRefEntry)o).name.equals(resname)) { list.setSelectedValue(o, true); break; } } } JButton bUpdate = new JButton("Update value", Icons.getIcon(Icons.ICON_REFRESH_16)); bUpdate.addActionListener(container); bUpdate.setActionCommand(StructViewer.UPDATE_VALUE); bView = new JButton("View/Edit", Icons.getIcon(Icons.ICON_ZOOM_16)); bView.addActionListener(this); bView.setEnabled(list.getSelectedValue() != null && list.getSelectedValue() != NONE && ((ResourceRefEntry)list.getSelectedValue()).entry != null); list.addListSelectionListener(this); 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.gridheight = 2; gbl.setConstraints(list, gbc); panel.add(list); gbc.gridheight = 1; gbc.weightx = 0.0; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets(3, 6, 3, 0); gbc.anchor = GridBagConstraints.SOUTH; gbc.gridwidth = GridBagConstraints.REMAINDER; gbl.setConstraints(bUpdate, gbc); panel.add(bUpdate); gbc.gridx = 1; gbc.gridy = 1; gbc.anchor = GridBagConstraints.NORTH; gbl.setConstraints(bView, gbc); panel.add(bView); panel.setMinimumSize(DIM_MEDIUM); panel.setPreferredSize(DIM_MEDIUM); return panel; } @Override public void select() { list.ensureIndexIsVisible(list.getSelectedIndex()); } @Override public boolean updateValue(AbstractStruct struct) { Object selected = list.getSelectedValue(); if (selected == NONE) { resname = NONE; // notifying listeners fireValueUpdated(new UpdateEvent(this, struct)); return true; } ResourceEntry entry = ((ResourceRefEntry)selected).entry; if (entry == null) { resname = ((ResourceRefEntry)selected).name; } else { int i = -1; for (int j = 0; j < type.length && i == -1; j++) { i = entry.toString().indexOf('.' + type[j].toUpperCase(Locale.ENGLISH)); if (i != -1) { resname = entry.toString().substring(0, i); curtype = type[j]; } } if (i == -1) { return false; } } // notifying listeners fireValueUpdated(new UpdateEvent(this, struct)); return true; } // --------------------- End Interface Editable --------------------- // --------------------- Begin Interface ListSelectionListener --------------------- @Override public void valueChanged(ListSelectionEvent e) { bView.setEnabled(list.getSelectedValue() != null && list.getSelectedValue() != NONE && ((ResourceRefEntry)list.getSelectedValue()).entry != null); } // --------------------- End Interface ListSelectionListener --------------------- // --------------------- Begin Interface Writeable --------------------- @Override public void write(OutputStream os) throws IOException { if (resname.equalsIgnoreCase(NONE)) { buffer.position(0); String s = StreamUtils.readString(buffer, buffer.limit()); buffer.position(0); if (s.equalsIgnoreCase(NONE)) { StreamUtils.writeBytes(os, buffer); } else { StreamUtils.writeBytes(os, (byte)0, buffer.limit()); } } else { StreamUtils.writeString(os, resname, getSize()); } } // --------------------- End Interface Writeable --------------------- //--------------------- Begin Interface Readable --------------------- @Override public int read(ByteBuffer buffer, int offset) { StreamUtils.copyBytes(buffer, offset, this.buffer, 0, getSize()); this.buffer.position(0); String s = StreamUtils.readString(this.buffer, this.buffer.limit()); if (s.isEmpty() || s.equalsIgnoreCase(NONE)) { resname = NONE; } else { resname = s.toUpperCase(Locale.ENGLISH); if (resname.equalsIgnoreCase(NONE)) { resname = NONE; } } // determine the correct file extension if (!resname.equals(NONE)) { for (int i = 0; i < this.type.length; i++) { if (null != ResourceFactory.getResourceEntry(resname + "." + this.type[i], true)) { curtype = this.type[i]; break; } } } return offset + getSize(); } //--------------------- End Interface Readable --------------------- @Override public String toString() { if (resname.equals(NONE)) return resname; String searchName = getSearchName(); if (searchName != null) return new StringBuffer(getResourceName()).append(" (").append(searchName).append(')').toString(); return getResourceName(); } //--------------------- Begin Interface IsTextual --------------------- @Override public String getText() { return resname; } //--------------------- End Interface IsTextual --------------------- //--------------------- Begin Interface IsReference --------------------- @Override public String getResourceName() { if (resname.equals(NONE)) { return resname; } else { return new StringBuffer(resname).append('.').append(curtype).toString(); } } //--------------------- End Interface IsReference --------------------- public boolean isEmpty() { return (resname.equals(NONE)); } public String getSearchName() { ResourceEntry entry = ResourceFactory.getResourceEntry(getResourceName(), true); if (entry != null) return entry.getSearchString(); return null; } public String getType() { return curtype; } public boolean isLegalEntry(ResourceEntry entry) { return entry.toString().lastIndexOf('.') != 0; } void addExtraEntries(List<Object> entries) { } // -------------------------- INNER CLASSES -------------------------- static final class ResourceRefEntry { private final ResourceEntry entry; private final String name; private ResourceRefEntry(ResourceEntry entry) { this.entry = entry; String string = entry.toString(); String search = entry.getSearchString(); if (search == null || BrowserMenuBar.getInstance().getResRefMode() == BrowserMenuBar.RESREF_ONLY) name = string; else if (BrowserMenuBar.getInstance().getResRefMode() == BrowserMenuBar.RESREF_REF_NAME) name = string + " (" + search + ')'; else name = search + " (" + string + ')'; } ResourceRefEntry(String name) { this.name = name; entry = null; } @Override public String toString() { return name; } } private static class IgnoreCaseExtComparator implements Comparator<Object> { @Override public int compare(Object o1, Object o2) { if (o1 != null && o2 != null) { String s1 = o1.toString(); String s2 = o2.toString(); int i1 = s1.lastIndexOf('.') > 0 ? s1.lastIndexOf('.') : s1.length(); int i2 = s2.lastIndexOf('.') > 0 ? s2.lastIndexOf('.') : s2.length(); return s1.substring(0, i1).compareToIgnoreCase(s2.substring(0, i2)); } else { return 0; } } @Override public boolean equals(Object obj) { return obj.equals(this); } } }