Skip to content

Commit

Permalink
Improve display of (non-)player characters in View tab of GAM resources
Browse files Browse the repository at this point in the history
Shows actual character names instead of garbled internal names (BG2, EE)
or internal structure data (BG1, PST).
  • Loading branch information
Argent77 committed Jul 12, 2023
1 parent 5e1e08e commit 2933128
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 39 deletions.
154 changes: 134 additions & 20 deletions src/org/infinity/gui/ViewerUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.function.Function;

import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
Expand Down Expand Up @@ -286,6 +287,19 @@ public static JLabel makeImagePanel(ResourceRef imageRef, boolean searchExtraDir
return new JLabel("No " + imageRef.getName().toLowerCase(Locale.ENGLISH), SwingConstants.CENTER);
}

/**
* Creates panel with the name, list control and button for edit selected list element.
*
* @param title Name of the panel
* @param struct Structure, which attributes must be shown in the returned editor
* @param listClass List will contain all attributes of {@code struct} with this class
*
* @return Editor for show list of the specified attributes
*/
public static JPanel makeListPanel(String title, AbstractStruct struct, Class<? extends StructEntry> listClass) {
return new StructListPanel(title, struct, listClass, null, null, null);
}

/**
* Creates panel with the name, list control and button for edit selected list element.
*
Expand All @@ -294,21 +308,92 @@ public static JLabel makeImagePanel(ResourceRef imageRef, boolean searchExtraDir
* @param listClass List will contain all attributes of {@code struct} with this class
* @param attrName Name of attribute in the {@code listClass}, used to show in the list
*
* @return Editor for show list of the specified attrubutes
* @return Editor for show list of the specified attributes
*/
public static JPanel makeListPanel(String title, AbstractStruct struct, Class<? extends StructEntry> listClass,
String attrName) {
return new StructListPanel(title, struct, listClass, attrName, null, null);
return new StructListPanel(title, struct, listClass, getAttributeEntry(attrName), null, null);
}

/**
* Creates panel with the name, list control and button for edit selected list element.
*
* @param title Name of the panel
* @param struct Structure, which attributes must be shown in the returned editor
* @param listClass List will contain all attributes of {@code struct} with this class
* @param attrName Name of attribute in the {@code listClass}, used to show in the list
* @param renderer A custom renderer for displaying list entries.
*
* @return Editor for show list of the specified attributes
*/
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);
return new StructListPanel(title, struct, listClass, getAttributeEntry(attrName), renderer, null);
}

/**
* Creates panel with the name, list control and button for edit selected list element.
*
* @param title Name of the panel
* @param struct Structure, which attributes must be shown in the returned editor
* @param listClass List will contain all attributes of {@code struct} with this class
* @param attrName Name of attribute in the {@code listClass}, used to show in the list
* @param renderer A custom renderer for displaying list entries.
* @param listener A custom {@link ListSelectionListener} that reacts to list selection events.
*
* @return Editor for show list of the specified attributes
*/
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);
return new StructListPanel(title, struct, listClass, getAttributeEntry(attrName), renderer, listener);
}

/**
* Creates panel with the name, list control and button for edit selected list element.
*
* @param title Name of the panel
* @param struct Structure, which attributes must be shown in the returned editor
* @param listClass List will contain all attributes of {@code struct} with this class
* @param attrEntry A function that retrieves and returns a {@code StructEntry} to be displayed in the list.
*
* @return Editor for show list of the specified attributes
*/
public static JPanel makeListPanel(String title, AbstractStruct struct, Class<? extends StructEntry> listClass,
AttributeEntry attrEntry) {
return new StructListPanel(title, struct, listClass, attrEntry, null, null);
}

/**
* Creates panel with the name, list control and button for edit selected list element.
*
* @param title Name of the panel
* @param struct Structure, which attributes must be shown in the returned editor
* @param listClass List will contain all attributes of {@code struct} with this class
* @param attrEntry A function that retrieves and returns a {@code StructEntry} to be displayed in the list.
* @param renderer A custom renderer for displaying list entries.
*
* @return Editor for show list of the specified attributes
*/
public static JPanel makeListPanel(String title, AbstractStruct struct, Class<? extends StructEntry> listClass,
AttributeEntry attrEntry, ListCellRenderer<Object> renderer) {
return new StructListPanel(title, struct, listClass, attrEntry, renderer, null);
}

/**
* Creates panel with the name, list control and button for edit selected list element.
*
* @param title Name of the panel
* @param struct Structure, which attributes must be shown in the returned editor
* @param listClass List will contain all attributes of {@code struct} with this class
* @param attrEntry A function that retrieves and returns a {@code StructEntry} to be displayed in the list.
* @param renderer A custom renderer for displaying list entries.
* @param listener A custom {@link ListSelectionListener} that reacts to list selection events.
*
* @return Editor for show list of the specified attributes
*/
public static JPanel makeListPanel(String title, AbstractStruct struct, Class<? extends StructEntry> listClass,
AttributeEntry attrEntry, ListCellRenderer<Object> renderer, ListSelectionListener listener) {
return new StructListPanel(title, struct, listClass, attrEntry, renderer, listener);
}

/**
Expand Down Expand Up @@ -428,11 +513,41 @@ public static JLabel createUrlLabel(String text, String url, int horizontalAlign
return l;
}

/**
* Creates a functional object that derives a {@link StructEntry} instance with the specified {@code attrName}
* from a given {@link StructEntry} input object.
*
* @param attrName Name of the attribute to find and return.
* @return A {@link AttributeEntry} object if {@code attrName} parameter is not {@code null}, {@code null} otherwise.
*/
private static AttributeEntry getAttributeEntry(String attrName) {
if (attrName != null) {
return (item) -> (item instanceof AbstractStruct) ? ((AbstractStruct) item).getAttribute(attrName) : item;
} else {
return null;
}
}

private ViewerUtil() {
}

// -------------------------- INNER CLASSES --------------------------

/**
* A functional interface that is used to find a {@link StructEntry} attribute from a given resource or substructure
* for display in the list.
*
* <p>
* <strong>Types:</strong>
* <ul>
* <li><strong>Parameter:</strong> {@code StructEntry} that contains the attribute for display.</li>
* <li><strong>Return value:</strong> {@code StructEntry} that is used for display in the list.</li>
* </ul>
* </p>
*/
public static interface AttributeEntry extends Function<StructEntry, StructEntry> {
}

public static final class StructListPanel extends JPanel implements TableModelListener, ActionListener {
private final AbstractStruct struct;
private final Class<? extends StructEntry> listClass;
Expand All @@ -441,7 +556,7 @@ public static final class StructListPanel extends JPanel implements TableModelLi
private final JButton bOpen = new JButton("View/Edit", Icons.ICON_ZOOM_16.getIcon());

private StructListPanel(String title, AbstractStruct struct, Class<? extends StructEntry> listClass,
String attrName, ListCellRenderer<Object> renderer, ListSelectionListener listener) {
AttributeEntry attrEntry, ListCellRenderer<Object> renderer, ListSelectionListener listener) {
super(new BorderLayout(0, 3));
this.struct = struct;
this.listClass = listClass;
Expand All @@ -454,23 +569,23 @@ private StructListPanel(String title, AbstractStruct struct, Class<? extends Str
if (renderer != null) {
list.setCellRenderer(renderer);
}
if (attrName == null) {
if (attrEntry == null) {
for (final StructEntry o : struct.getFields()) {
if (o.getClass() == listClass) {
listModel.addElement(o);
}
}
} else {
if (renderer == null) {
list.setCellRenderer(new StructListRenderer(attrName));
list.setCellRenderer(new StructListRenderer(attrEntry));
}
final List<AbstractStruct> templist = new ArrayList<>();
for (final StructEntry o : struct.getFields()) {
if (o.getClass() == listClass) {
templist.add((AbstractStruct) o);
}
}
Collections.sort(templist, new StructListComparator(attrName));
Collections.sort(templist, new StructListComparator(attrEntry));
for (AbstractStruct s : templist) {
listModel.addElement(s);
}
Expand Down Expand Up @@ -569,11 +684,11 @@ public static class StructListRenderer extends DefaultListCellRenderer implement
org.infinity.resource.sto.ItemSale11.class
);

private final String attrName;
private final AttributeEntry attrEntry;
private final boolean showIcons;

public StructListRenderer(String attrName) {
this.attrName = attrName;
public StructListRenderer(AttributeEntry attrEntry) {
this.attrEntry = attrEntry;
this.showIcons = BrowserMenuBar.getInstance().getOptions().showResourceListIcons();
}

Expand All @@ -595,14 +710,13 @@ public Component getListCellRendererComponent(JList<?> list, Object value, int i

@Override
public String getListValue(Object value) {
if (value instanceof AbstractStruct) {
AbstractStruct effect = (AbstractStruct) value;
StructEntry entry = effect.getAttribute(attrName);
if (value instanceof StructEntry) {
final StructEntry entry = attrEntry.apply((StructEntry) value);
if (entry instanceof ResourceRef) {
ResourceRef resRef = (ResourceRef) entry;
return resRef.getSearchName() + " (" + resRef.getResourceName() + ')';
} else if (entry == null || entry.toString().trim().isEmpty()) {
return effect.toString();
return value.toString();
} else if (entry != null) {
return entry.toString();
}
Expand Down Expand Up @@ -640,7 +754,7 @@ private Icon loadIcon(AbstractStruct struct) {
// Resource-specific icon
for (final Class<? extends AbstractStruct> as : SUPPORTED_STRUCTURES) {
if (as.isAssignableFrom(struct.getClass())) {
se = struct.getAttribute(attrName);
se = attrEntry.apply(struct);
break;
}
}
Expand All @@ -659,15 +773,15 @@ private Icon loadIcon(AbstractStruct struct) {
}

private static final class StructListComparator implements Comparator<AbstractStruct> {
private final String attrName;
private final AttributeEntry attrEntry;

private StructListComparator(String attrName) {
this.attrName = attrName;
private StructListComparator(AttributeEntry attrEntry) {
this.attrEntry = attrEntry;
}

@Override
public int compare(AbstractStruct as1, AbstractStruct as2) {
return as1.getAttribute(attrName).toString().compareTo(as2.getAttribute(attrName).toString());
return attrEntry.apply(as1).toString().compareTo(attrEntry.apply(as2).toString());
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/org/infinity/resource/cre/Viewer.java
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ private JPanel makeItemSpellsPanel(CreResource cre) {
private JPanel makeItemSpellsPanelIWD2(CreResource cre) {
JPanel panel = new JPanel(new GridLayout(1, 2, 6, 0));
panel.add(new ViewerItems(cre));
panel.add(ViewerUtil.makeListPanel("Spells/abilities (# known)", cre, Iwd2Struct.class, null,
panel.add(ViewerUtil.makeListPanel("Spells/abilities (# known)", cre, Iwd2Struct.class, (String) null,
new SpellListRendererIWD2()));
panel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
return panel;
Expand Down
53 changes: 36 additions & 17 deletions src/org/infinity/resource/gam/Viewer.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,47 @@

import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;

import org.infinity.datatype.Flag;
import org.infinity.datatype.ResourceRef;
import org.infinity.gui.ViewerUtil;
import org.infinity.gui.ViewerUtil.ListValueRenderer;
import org.infinity.resource.AbstractStruct;
import org.infinity.resource.AbstractVariable;
import org.infinity.resource.Profile;
import org.infinity.resource.StructEntry;
import org.infinity.resource.cre.CreResource;

final class Viewer extends JPanel {
/** A function that determines the name of (non-)player characters in GAM resources. */
private static final ViewerUtil.AttributeEntry NPC_ENTRY = (struct) -> {
if (struct instanceof PartyNPC) {
final PartyNPC npc = (PartyNPC) struct;

StructEntry se = npc.getAttribute(PartyNPC.GAM_NPC_NAME);
if (se != null && !se.toString().isEmpty()) {
// Display character name from PartyNPC structure
return se;
}

se = npc.getAttribute(PartyNPC.GAM_NPC_CRE_RESOURCE);
if (se instanceof CreResource) {
se = ((CreResource) se).getAttribute(CreResource.CRE_NAME);
if (se != null) {
// Display character name from embedded CRE resource
return se;
}
} else if (se instanceof ResourceRef) {
// Display character info from CRE resref
return se;
}
}

// Fall-back option: Display original structure
return struct;
};

private static JPanel makeMiscPanel(GamResource gam) {
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
Expand Down Expand Up @@ -67,18 +95,8 @@ private static JPanel makeMiscPanel(GamResource gam) {
}

Viewer(GamResource gam) {
JPanel stats1Panel, stats2Panel;
if (Profile.getEngine() == Profile.Engine.PST || Profile.getEngine() == Profile.Engine.BG1) {
stats1Panel = ViewerUtil.makeListPanel("Non-player characters", gam, NonPartyNPC.class, null);
stats2Panel = ViewerUtil.makeListPanel("Player characters", gam, PartyNPC.class, null);
} else if (Profile.getEngine() == Profile.Engine.IWD || Profile.getEngine() == Profile.Engine.IWD2) {
stats1Panel = ViewerUtil.makeListPanel("Non-player characters", gam, NonPartyNPC.class, PartyNPC.GAM_NPC_NAME);
stats2Panel = ViewerUtil.makeListPanel("Player characters", gam, PartyNPC.class, PartyNPC.GAM_NPC_NAME);
} else {
stats1Panel = ViewerUtil.makeListPanel("Non-player characters", gam, NonPartyNPC.class,
PartyNPC.GAM_NPC_CHARACTER);
stats2Panel = ViewerUtil.makeListPanel("Player characters", gam, PartyNPC.class, PartyNPC.GAM_NPC_CHARACTER);
}
final JPanel stats1Panel = ViewerUtil.makeListPanel("Non-player characters", gam, NonPartyNPC.class, NPC_ENTRY);
final JPanel stats2Panel = ViewerUtil.makeListPanel("Player characters", gam, PartyNPC.class, NPC_ENTRY);

JPanel var1Panel = ViewerUtil.makeListPanel("Variables", gam, Variable.class, AbstractVariable.VAR_NAME,
new VariableListRenderer());
Expand All @@ -95,14 +113,15 @@ private static JPanel makeMiscPanel(GamResource gam) {

private static final class VariableListRenderer extends DefaultListCellRenderer implements ListValueRenderer {
private VariableListRenderer() {
super();
}

@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected,
boolean cellHasFocus) {
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
label.setText(getListValue(value));
return label;
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
setText(getListValue(value));
return this;
}

@Override
Expand Down
2 changes: 1 addition & 1 deletion src/org/infinity/resource/sto/Viewer.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ private static JPanel makeFieldPanel(StoResource sto) {
}
JPanel curePanel = ViewerUtil.makeListPanel("Cures for sale", sto, Cure.class, Cure.STO_CURE_SPELL);
JPanel drinkPanel = ViewerUtil.makeListPanel("Drinks for sale", sto, Drink.class, Drink.STO_DRINK_NAME);
JPanel buyPanel = ViewerUtil.makeListPanel("Items purchased", sto, Purchases.class, null);
JPanel buyPanel = ViewerUtil.makeListPanel("Items purchased", sto, Purchases.class);
JPanel flagsPanel = ViewerUtil.makeCheckPanel((Flag) sto.getAttribute(StoResource.STO_FLAGS), 1);
JPanel fieldPanel = makeFieldPanel(sto);

Expand Down

0 comments on commit 2933128

Please sign in to comment.