// Near Infinity - An Infinity Engine Browser and Editor
// Copyright (C) 2001 - 2005 Jon Olav Hauglid
// See LICENSE.txt for license information
package org.infinity.gui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
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.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import org.infinity.datatype.Flag;
import org.infinity.datatype.ResourceRef;
import org.infinity.datatype.StringRef;
import org.infinity.icon.Icons;
import org.infinity.resource.AbstractStruct;
import org.infinity.resource.Resource;
import org.infinity.resource.ResourceFactory;
import org.infinity.resource.StructEntry;
import org.infinity.resource.Viewable;
import org.infinity.resource.graphics.BamDecoder;
import org.infinity.resource.graphics.GraphicsResource;
import org.infinity.resource.graphics.MosResource;
import org.infinity.resource.graphics.BamDecoder.BamControl;
import org.infinity.resource.key.ResourceEntry;
import org.infinity.util.SimpleListModel;
public final class ViewerUtil
{
public static void addLabelFieldPair(JPanel panel, StructEntry entry, GridBagLayout gbl,
GridBagConstraints gbc, boolean endline)
{
if (entry == null)
return;
JLabel label = new JLabel(entry.getName());
JComponent text;
if (entry instanceof ResourceRef) {
text = new LinkButton((ResourceRef)entry);
} else {
if (entry instanceof StringRef) {
text = new JLabel(((StringRef)entry).toString(BrowserMenuBar.getInstance().showStrrefs()));
} else {
text = new JLabel(entry.toString());
}
text.setFont(text.getFont().deriveFont(Font.PLAIN));
}
gbc.weightx = 0.0;
gbc.fill = GridBagConstraints.NONE;
gbc.gridwidth = 1;
gbc.anchor = GridBagConstraints.WEST;
gbl.setConstraints(label, gbc);
panel.add(label);
gbc.weightx = 1.0;
gbc.fill = GridBagConstraints.HORIZONTAL;
if (endline)
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbl.setConstraints(text, gbc);
panel.add(text);
}
public static void addLabelFieldPair(JPanel panel, String name, String field, GridBagLayout gbl,
GridBagConstraints gbc, boolean endline)
{
if (name != null) {
JLabel label = new JLabel(name);
JComponent text = new JLabel((field != null) ? field : "");
text.setFont(text.getFont().deriveFont(Font.PLAIN));
gbc.weightx = 0.0;
gbc.fill = GridBagConstraints.NONE;
gbc.gridwidth = 1;
gbc.anchor = GridBagConstraints.WEST;
gbl.setConstraints(label, gbc);
panel.add(label);
gbc.weightx = 1.0;
gbc.fill = GridBagConstraints.HORIZONTAL;
if (endline)
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbl.setConstraints(text, gbc);
panel.add(text);
}
}
public static JLabel makeBamPanel(ResourceRef iconRef, int frameNr)
{
ResourceEntry iconEntry = ResourceFactory.getResourceEntry(iconRef.getResourceName());
if (iconEntry != null) {
try {
BamDecoder decoder = BamDecoder.loadBam(iconEntry);
BamControl ctrl = decoder.createControl();
JLabel label = new JLabel(iconRef.getName(), JLabel.CENTER);
frameNr = Math.min(frameNr, decoder.frameCount() - 1);
label.setIcon(new ImageIcon(decoder.frameGet(ctrl, frameNr)));
label.setVerticalTextPosition(SwingConstants.BOTTOM);
label.setHorizontalTextPosition(SwingConstants.CENTER);
return label;
} catch (Exception e) {
e.printStackTrace();
}
}
return new JLabel("No " + iconRef.getName().toLowerCase(Locale.ENGLISH), JLabel.CENTER);
}
public static JComponent makeBamPanel(ResourceRef iconRef, int animNr, int frameNr)
{
ResourceEntry iconEntry = ResourceFactory.getResourceEntry(iconRef.getResourceName());
if (iconEntry != null) {
try {
BamDecoder decoder = BamDecoder.loadBam(iconEntry);
BamControl ctrl = decoder.createControl();
JLabel label = new JLabel(iconRef.getName(), JLabel.CENTER);
int frameIdx = -1;
for (int curAnimIdx = animNr; curAnimIdx >= 0 && frameIdx < 0; curAnimIdx--) {
for (int curFrameIdx = frameNr; curFrameIdx >= 0 && frameIdx < 0; curFrameIdx--) {
frameIdx = ctrl.cycleGetFrameIndexAbsolute(curAnimIdx, curFrameIdx);
}
}
label.setIcon(new ImageIcon(decoder.frameGet(ctrl, frameIdx)));
label.setVerticalTextPosition(SwingConstants.BOTTOM);
label.setHorizontalTextPosition(SwingConstants.CENTER);
return label;
} catch (Exception e) {
e.printStackTrace();
}
}
return new JLabel("No " + iconRef.getName().toLowerCase(Locale.ENGLISH), JLabel.CENTER);
}
public static JComponent makeCheckLabel(StructEntry entry, String yes)
{
JLabel check = new JLabel(entry.getName());
if (entry.toString().equalsIgnoreCase(yes))
check.setIcon(Icons.getIcon(Icons.ICON_CHECK_16));
else
check.setIcon(Icons.getIcon(Icons.ICON_CHECK_NOT_16));
return check;
}
public static JPanel makeCheckPanel(Flag flag, int rows)
{
JPanel panel = new JPanel(new GridLayout(0, rows, 3, 3));
for (int i = 0; i < flag.getSize() << 3; i++) {
String s = flag.getString(i);
if (s != null && !s.equals("") && !s.startsWith("Unknown")) {
JLabel check = new JLabel(flag.getString(i));
if (flag.isFlagSet(i))
check.setIcon(Icons.getIcon(Icons.ICON_CHECK_16));
else
check.setIcon(Icons.getIcon(Icons.ICON_CHECK_NOT_16));
panel.add(check);
}
}
panel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(flag.getName()),
BorderFactory.createEmptyBorder(3, 3, 3, 3)));
return panel;
}
public static JLabel makeImagePanel(ResourceRef imageRef)
{
ResourceEntry imageEntry = ResourceFactory.getResourceEntry(imageRef.getResourceName());
if (imageEntry != null) {
Resource resource = ResourceFactory.getResource(imageEntry);
if (resource != null) {
JLabel label = new JLabel(imageRef.getName(), JLabel.CENTER);
label.setVerticalTextPosition(SwingConstants.BOTTOM);
label.setHorizontalTextPosition(SwingConstants.CENTER);
if (resource instanceof GraphicsResource) {
label.setIcon(new ImageIcon(((GraphicsResource)resource).getImage()));
} else if (resource instanceof MosResource) {
label.setIcon(new ImageIcon(((MosResource)resource).getImage()));
}
return label;
}
}
return new JLabel("No " + imageRef.getName().toLowerCase(Locale.ENGLISH), JLabel.CENTER);
}
public static JPanel makeListPanel(String title, AbstractStruct struct,
Class<? extends StructEntry> listClass, String attrName)
{
return new StructListPanel(title, struct, listClass, attrName, null, null);
}
public static JPanel makeListPanel(String title, AbstractStruct struct,
Class<? extends StructEntry> listClass,
String attrName, ListCellRenderer<Object> renderer)
{
return new StructListPanel(title, struct, listClass, attrName, renderer, null);
}
public static JPanel makeListPanel(String title, AbstractStruct struct,
Class<? extends StructEntry> listClass, String attrName,
ListCellRenderer<Object> renderer, ListSelectionListener listener)
{
return new StructListPanel(title, struct, listClass, attrName, renderer, listener);
}
public static JPanel makeTextAreaPanel(StructEntry entry)
{
String text;
if (entry instanceof StringRef) {
text = ((StringRef)entry).toString(BrowserMenuBar.getInstance().showStrrefs());
} else {
text = entry.toString();
}
InfinityTextArea ta = new InfinityTextArea(text, true);
ta.setCaretPosition(0);
ta.setHighlightCurrentLine(false);
ta.setEditable(false);
ta.setLineWrap(true);
ta.setWrapStyleWord(true);
InfinityScrollPane scroll = new InfinityScrollPane(ta, true);
scroll.setLineNumbersEnabled(false);
ta.setMargin(new Insets(3, 3, 3, 3));
JPanel panel = new JPanel(new BorderLayout());
panel.add(new JLabel(entry.getName()), BorderLayout.NORTH);
panel.add(scroll, BorderLayout.CENTER);
panel.setPreferredSize(new Dimension(5, 5));
return panel;
}
/** Initializes a {@link GridBagConstraints} instance. */
public static GridBagConstraints setGBC(GridBagConstraints gbc, int gridX, int gridY,
int gridWidth, int gridHeight, double weightX, double weightY,
int anchor, int fill, Insets insets, int iPadX, int iPadY)
{
if (gbc == null) gbc = new GridBagConstraints();
gbc.gridx = gridX;
gbc.gridy = gridY;
gbc.gridwidth = gridWidth;
gbc.gridheight = gridHeight;
gbc.weightx = weightX;
gbc.weighty = weightY;
gbc.anchor = anchor;
gbc.fill = fill;
gbc.insets = (insets == null) ? new Insets(0, 0, 0, 0) : insets;
gbc.ipadx = iPadX;
gbc.ipady = iPadY;
return gbc;
}
/** Returns a JLabel control containing a clickable link. */
public static JLabel createUrlLabel(String url)
{
return createUrlLabel(url, url, SwingConstants.LEADING);
}
/** Returns a JLabel control containing a clickable link. */
public static JLabel createUrlLabel(String text, String url)
{
return createUrlLabel(text, url, SwingConstants.LEADING);
}
/** Returns a JLabel control containing a clickable link. */
public static JLabel createUrlLabel(String text, String url, int horizontalAlignment)
{
JLabel l = new JLabel("<html><a href=\"" + url + "\">" + text + "</a></html>", horizontalAlignment);
l.addMouseListener(new UrlBrowser(url));
l.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
return l;
}
private ViewerUtil(){}
// -------------------------- INNER CLASSES --------------------------
public static final class StructListPanel extends JPanel implements TableModelListener, ActionListener
{
private final AbstractStruct struct;
private final Class<? extends StructEntry> listClass;
private final JList<Object> list;
private final SimpleListModel<Object> listModel = new SimpleListModel<Object>();
private final JButton bOpen = new JButton("View/Edit", Icons.getIcon(Icons.ICON_ZOOM_16));
private Comparator<AbstractStruct> comp;
private StructListPanel(String title, AbstractStruct struct,
Class<? extends StructEntry> listClass, String attrName,
ListCellRenderer<Object> renderer, ListSelectionListener listener)
{
super(new BorderLayout(0, 3));
this.struct = struct;
this.listClass = listClass;
struct.addTableModelListener(this);
list = new JList<>(listModel);
if (listener != null)
list.addListSelectionListener(listener);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
if (renderer != null)
list.setCellRenderer(renderer);
if (attrName == null) {
for (int i = 0; i < struct.getFieldCount(); i++) {
StructEntry o = struct.getField(i);
if (o.getClass() == listClass)
listModel.addElement(o);
}
}
else {
if (renderer == null)
list.setCellRenderer(new StructListRenderer(attrName));
List<AbstractStruct> templist = new ArrayList<AbstractStruct>();
for (int i = 0; i < struct.getFieldCount(); i++) {
StructEntry o = struct.getField(i);
if (o.getClass() == listClass)
templist.add((AbstractStruct)o);
}
comp = new StructListComparator(attrName);
Collections.sort(templist, comp);
for (int i = 0; i < templist.size(); i++)
listModel.addElement(templist.get(i));
}
final JPanel parent = this;
list.addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
if (e.getClickCount() == 2 && list.getSelectedValue() instanceof Viewable) {
new ViewFrame(parent.getTopLevelAncestor(), (Viewable)list.getSelectedValue());
}
}
});
if (listModel.size() > 0)
list.setSelectedIndex(0);
bOpen.addActionListener(this);
bOpen.setEnabled(listModel.size() > 0 && listModel.get(0) instanceof Viewable);
add(new JLabel(title), BorderLayout.NORTH);
add(new JScrollPane(list), BorderLayout.CENTER);
add(bOpen, BorderLayout.SOUTH);
setPreferredSize(new Dimension(5, 5));
}
/** Provides access to the list component of the panel. */
public JList<Object> getList() { return list; }
@Override
public void actionPerformed(ActionEvent event)
{
new ViewFrame(getTopLevelAncestor(), (Viewable)list.getSelectedValue());
}
@Override
public void tableChanged(TableModelEvent event)
{
if (event.getType() == TableModelEvent.DELETE) {
// go through the list and find what was deleted
List<StructEntry> structlist = struct.getList();
for (int i = 0; i < listModel.size(); i++) {
if (!structlist.contains(listModel.get(i))) {
listModel.remove(i);
i--;
}
}
/*
// Ineffective - any better solutions?
if (comp == null) {
listModel.clear();
for (int i = 0; i < struct.getRowCount(); i++) {
StructEntry o = struct.getStructEntryAt(i);
if (o.getClass() == listClass)
listModel.addElement(o);
}
}
else {
List<AbstractStruct> templist = new ArrayList<AbstractStruct>();
for (int i = 0; i < struct.getRowCount(); i++) {
StructEntry o = struct.getStructEntryAt(i);
if (o.getClass() == listClass)
templist.add((AbstractStruct)o);
}
Collections.sort(templist, comp);
listModel.clear();
for (int i = 0; i < templist.size(); i++) {
listModel.addElement(templist.get(i));
}
}
*/
if (listModel.size() > 0)
list.setSelectedIndex(0);
bOpen.setEnabled(listModel.size() > 0 && listModel.get(0) instanceof Viewable);
}
else if (event.getType() == TableModelEvent.INSERT) {
for (int i = event.getFirstRow(); i <= event.getLastRow(); i++) {
if (i >= struct.getFieldCount()) {
break;
}
Object o = struct.getField(i);
if (o.getClass() == listClass) {
listModel.addElement(o); // Not sorted properly after this...
if (!bOpen.isEnabled() && listModel.get(0) instanceof Viewable) {
bOpen.setEnabled(true);
list.setSelectedIndex(0);
}
}
}
}
}
}
private static final class StructListRenderer extends DefaultListCellRenderer
{
private final String attrName;
private StructListRenderer(String attrName)
{
this.attrName = attrName;
}
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected,
boolean cellHasFocus)
{
JLabel label = (JLabel)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
AbstractStruct effect = (AbstractStruct)value;
StructEntry entry = effect.getAttribute(attrName);
if (entry instanceof ResourceRef) {
ResourceRef resref = (ResourceRef)entry;
label.setText(resref.getSearchName() + " (" + resref.getResourceName() + ')');
}
else if (entry == null || entry.toString().trim().equals(""))
label.setText(effect.toString());
else
label.setText(entry.toString());
return label;
}
}
private static final class StructListComparator implements Comparator<AbstractStruct>
{
private final String attrName;
private StructListComparator(String attrName)
{
this.attrName = attrName;
}
@Override
public int compare(AbstractStruct as1, AbstractStruct as2)
{
return as1.getAttribute(attrName).toString().compareTo(as2.getAttribute(attrName).toString());
}
}
}